summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
committerYaco <franco@reevo.org>2020-06-04 11:01:00 -0300
commitfc7369835258467bf97eb64f184b93691f9a9fd5 (patch)
treedaabd60089d2dd76d9f5fb416b005fbe159c799d /bin
first commit
Diffstat (limited to 'bin')
-rwxr-xr-xbin/bkp/bkp_wiki.sh29
-rw-r--r--bin/wiki/ImportarImagenPrensa.php119
-rw-r--r--bin/wiki/ListarPaginas.php66
-rw-r--r--bin/wiki/PropiedadActualizar.php120
-rw-r--r--bin/wiki/PropiedadObtener.php80
-rw-r--r--bin/wiki/composer.json5
-rw-r--r--bin/wiki/composer.lock487
-rw-r--r--bin/wiki/initReevo.php11
-rw-r--r--bin/wiki/initReevoClass.php210
-rw-r--r--bin/wiki/reevoPages/int/Formulario:Audiovisual.mw90
-rw-r--r--bin/wiki/reevoPages/int/Formulario:Experiencia.mw181
-rw-r--r--bin/wiki/reevoPages/int/Formulario:Prensa.mw64
-rw-r--r--bin/wiki/reevoPages/int/MediaWiki:Mainpage.mw1
-rw-r--r--bin/wiki/reevoPages/int/MediaWiki:Sidebar.mw15
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Audiovisual.mw72
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Experiencia.mw131
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Prensa-Archivo.mw13
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Prensa-Vista.mw21
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Prensa.mw49
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfil.mw8
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfilMsg.mw3
-rw-r--r--bin/wiki/reevoPages/int/Plantilla:Usuario.mw25
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:autoria.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:duracion.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:estreno.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:genero.mw5
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:magnet.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:pais.mw258
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:poster.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:sub.mw6
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:titulo.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:url.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:urltrailer.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Audiovisual:web.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-email.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-telefono.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-url.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:descripcion.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:imagen-destacada.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-certificacion.mw5
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-contextosocial.mw8
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-corrientes.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-final.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-inicio.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-lucro.mw5
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-nivel.mw9
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-niveleconomico.mw7
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-participantes.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-tipoeducacion.mw6
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:info-virtual.mw6
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-calle.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-ciudad.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-pais.mw259
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-provincia.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:descripcion.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:elggid.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:etiquetas.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:fecha.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:fuente.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:idioma.mw6
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:imagen.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:pageid.mw1
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:pais.mw258
-rw-r--r--bin/wiki/reevoPages/int/Propiedad:Prensa:url.mw1
-rw-r--r--bin/wiki/reevoPages/int/REEVO:Audiovisual.mw1
-rw-r--r--bin/wiki/reevoPages/int/REEVO:Experiencia.mw1
-rw-r--r--bin/wiki/reevoPages/int/REEVO:Prensa.mw1
-rw-r--r--bin/wiki/reevoPages/int/REEVO:Usuario.mw1
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/.gitignore6
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/.phan/config.php40
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/.scrutinizer.yml13
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/.travis.yml61
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/LICENSE.md264
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/README.md16
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/RELEASENOTES.md95
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/install-mediawiki.sh29
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/run-webserver.sh25
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/composer.json52
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/Makefile225
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/conf.py80
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/index.rst17
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/make.bat281
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/multipart.rst28
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/overview.rst129
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/docs/quickstart.rst87
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api-base/phpcs.xml6
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/phpunit.xml.dist16
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php31
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiUser.php90
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php37
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php118
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php96
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php158
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php507
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php60
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php168
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php77
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/Request.php30
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/RsdException.php12
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php61
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/src/UsageException.php75
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/MediawikiApiTest.php103
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TestEnvironment.php92
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TokenHandlingTest.php28
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/ApiUserTest.php71
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/FluentRequestTest.php69
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/ClientFactoryTest.php91
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/MiddlewareFactoryTest.php214
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiApiTest.php296
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiSessionTest.php95
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MultipartRequestTest.php44
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/SimpleRequestTest.php49
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/UsageExceptionTest.php39
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/.gitignore9
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml13
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml52
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md264
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/README.md74
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md111
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh55
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/composer.json44
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile225
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst28
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py80
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst44
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst28
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst25
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat281
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst54
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst88
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst34
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml11
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist21
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php32
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php41
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php27
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php68
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php240
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php161
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php139
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php56
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php83
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php108
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php105
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php246
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php147
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php68
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php55
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php116
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php76
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php37
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php47
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php39
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php47
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php39
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php73
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php96
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php48
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php23
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php45
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php83
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php63
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php59
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ.pngbin0 -> 2807 bytes
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json241
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php203
-rwxr-xr-xbin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php75
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php86
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php66
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php99
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php116
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php35
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist18
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php26
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php55
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php54
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php187
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist13
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/.gitignore5
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/.scrutinizer.yml13
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/.travis.yml25
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/LICENSE.md264
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/README.md17
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/RELEASENOTES.md88
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/composer.json32
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/Makefile225
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/conf.py80
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/index.rst22
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/make.bat281
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/phpunit.xml.dist13
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Content.php83
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/EditInfo.php79
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/File.php37
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Log.php171
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/LogList.php138
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/NamespaceInfo.php131
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Page.php63
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/PageIdentifier.php85
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Pages.php99
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Redirect.php53
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revision.php106
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revisions.php102
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Title.php83
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/src/User.php140
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/ContentTest.php30
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/EditInfoTest.php51
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/FileTest.php44
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/LogListTest.php25
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/NamespaceInfoTest.php72
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageIdentifierTest.php54
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageTest.php48
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PagesTest.php44
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RedirectTest.php20
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionTest.php57
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionsTest.php41
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/TitleTest.php56
-rw-r--r--bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/UserTest.php59
-rw-r--r--bin/wiki/vendor/autoload.php7
-rw-r--r--bin/wiki/vendor/composer/ClassLoader.php445
-rw-r--r--bin/wiki/vendor/composer/LICENSE21
-rw-r--r--bin/wiki/vendor/composer/autoload_classmap.php9
-rw-r--r--bin/wiki/vendor/composer/autoload_files.php13
-rw-r--r--bin/wiki/vendor/composer/autoload_namespaces.php9
-rw-r--r--bin/wiki/vendor/composer/autoload_psr4.php16
-rw-r--r--bin/wiki/vendor/composer/autoload_real.php70
-rw-r--r--bin/wiki/vendor/composer/autoload_static.php75
-rw-r--r--bin/wiki/vendor/composer/installed.json489
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/CHANGELOG.md1287
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/LICENSE19
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/README.md91
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/UPGRADING.md1203
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/composer.json44
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Client.php422
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/ClientInterface.php84
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php314
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php84
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php90
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php71
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php403
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php27
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php7
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php37
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php13
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php217
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php27
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php7
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php4
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php4
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php565
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php27
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php45
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php199
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php92
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php189
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php55
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php532
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/HandlerStack.php273
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/MessageFormatter.php180
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Middleware.php255
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/Pool.php123
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php106
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php237
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/RequestOptions.php255
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php112
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/TransferStats.php126
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/UriTemplate.php237
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/functions.php333
-rw-r--r--bin/wiki/vendor/guzzlehttp/guzzle/src/functions_include.php6
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/CHANGELOG.md65
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/LICENSE19
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/Makefile13
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/README.md504
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/composer.json34
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/AggregateException.php16
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/CancellationException.php9
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/Coroutine.php151
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/EachPromise.php229
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/FulfilledPromise.php82
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/Promise.php280
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/PromiseInterface.php93
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/PromisorInterface.php15
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/RejectedPromise.php87
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/RejectionException.php47
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/TaskQueue.php66
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/TaskQueueInterface.php25
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/functions.php457
-rw-r--r--bin/wiki/vendor/guzzlehttp/promises/src/functions_include.php6
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/.editorconfig9
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/CHANGELOG.md225
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/LICENSE19
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/README.md745
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/composer.json45
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/AppendStream.php241
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/BufferStream.php137
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/CachingStream.php138
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/DroppingStream.php42
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/FnStream.php158
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/InflateStream.php52
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/LazyOpenStream.php39
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/LimitStream.php155
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/MessageTrait.php183
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/MultipartStream.php153
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/NoSeekStream.php22
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/PumpStream.php165
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/Request.php142
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/Response.php136
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/Rfc7230.php18
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/ServerRequest.php376
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/Stream.php270
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php149
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/StreamWrapper.php161
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/UploadedFile.php316
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/Uri.php738
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/UriNormalizer.php216
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/UriResolver.php219
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/functions.php898
-rw-r--r--bin/wiki/vendor/guzzlehttp/psr7/src/functions_include.php6
-rw-r--r--bin/wiki/vendor/psr/http-message/CHANGELOG.md36
-rw-r--r--bin/wiki/vendor/psr/http-message/LICENSE19
-rw-r--r--bin/wiki/vendor/psr/http-message/README.md13
-rw-r--r--bin/wiki/vendor/psr/http-message/composer.json26
-rw-r--r--bin/wiki/vendor/psr/http-message/src/MessageInterface.php187
-rw-r--r--bin/wiki/vendor/psr/http-message/src/RequestInterface.php129
-rw-r--r--bin/wiki/vendor/psr/http-message/src/ResponseInterface.php68
-rw-r--r--bin/wiki/vendor/psr/http-message/src/ServerRequestInterface.php261
-rw-r--r--bin/wiki/vendor/psr/http-message/src/StreamInterface.php158
-rw-r--r--bin/wiki/vendor/psr/http-message/src/UploadedFileInterface.php123
-rw-r--r--bin/wiki/vendor/psr/http-message/src/UriInterface.php323
-rw-r--r--bin/wiki/vendor/psr/log/.gitignore1
-rw-r--r--bin/wiki/vendor/psr/log/LICENSE19
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/AbstractLogger.php128
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/InvalidArgumentException.php7
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/LogLevel.php18
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareInterface.php18
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareTrait.php26
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/LoggerInterface.php123
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/LoggerTrait.php140
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/NullLogger.php28
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php144
-rw-r--r--bin/wiki/vendor/psr/log/Psr/Log/Test/TestLogger.php146
-rw-r--r--bin/wiki/vendor/psr/log/README.md52
-rw-r--r--bin/wiki/vendor/psr/log/composer.json26
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/.gitignore5
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/.travis.yml18
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/LICENSE21
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/README.md19
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/composer.json21
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/phpunit.xml22
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/src/getallheaders.php46
-rw-r--r--bin/wiki/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php121
351 files changed, 35590 insertions, 0 deletions
diff --git a/bin/bkp/bkp_wiki.sh b/bin/bkp/bkp_wiki.sh
new file mode 100755
index 00000000..c9aef4a4
--- /dev/null
+++ b/bin/bkp/bkp_wiki.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# ----
+# Copyright (C) 2013-2020 - Reevo Project (http://reevo.org)
+# License: Affero GPL version 3 - http://www.gnu.org/licenses/agpl.html
+# ES: Este archivos es parte de: reevo-web (http://git.reevo.org/reevo/reevo-web)
+# EN: This file is part of: reevo-web (http://git.reevo.org/reevo/reevo-web)
+# ----
+
+cd /srv/reevo-2020/bin/bkp/
+
+RUTA=`cat ../../etc/global_config.php | grep '^$REEVO_PATH' | cut -d '"' -f 2`
+DOMINIO=`cat $RUTA/etc/global_config.php | grep '^$REEVO_URL' | cut -d '"' -f 2`
+USER=`cat $RUTA/etc/global_config.php | grep '^$REEVO_DB_USER' | cut -d '"' -f 2`
+PASSWORD=`cat $RUTA/etc/global_config.php | grep '^$REEVO_DB_PASS' | cut -d '"' -f 2`
+
+DB=`cat $RUTA/etc/global_config.php | grep '^$REEVO_DB_WIKI' | cut -d '"' -f 2`
+TIMESTAMP=`date +%Y-%m-%d-%H:%M:%S`
+
+echo ""
+echo "Se va a generar una copia de la BD: $DB"
+mysqldump --user=$USER --password=$PASSWORD $DB > "$RUTA/bkp/wiki/$DOMINIO-$DB-$TIMESTAMP.sql"
+
+echo ""
+echo "Se comprime la BD"
+bzip2 "$RUTA/bkp/wiki/$DOMINIO-$DB-$TIMESTAMP.sql"
+
+echo "Generamos enlace simbolico"
+rm "$RUTA/bkp/wiki/ultimo"
+ln -s "$RUTA/bkp/wiki/$DOMINIO-$DB-$TIMESTAMP.sql.bz2" "$RUTA/bkp/wiki/ultimo"
diff --git a/bin/wiki/ImportarImagenPrensa.php b/bin/wiki/ImportarImagenPrensa.php
new file mode 100644
index 00000000..91a247e4
--- /dev/null
+++ b/bin/wiki/ImportarImagenPrensa.php
@@ -0,0 +1,119 @@
+<?php
+# ----
+# Copyright (C) 2013-2020 - Reevo Project (http://reevo.org)
+# License: Affero GPL version 3 - http://www.gnu.org/licenses/agpl.html
+# ES: Este script importa las imagenes de los objetos de prensa
+# ----
+
+// ini_set( 'post_max_size', '50M' );
+// ini_set( 'upload_max_filesize', '50M' );
+// error_reporting(0);
+// Load all the stuff
+require_once( __DIR__ . '/vendor/autoload.php' );
+require_once( __DIR__ .'/../../etc/global_config.php' );
+
+// Log in to a wiki
+$api = new \Mediawiki\Api\MediawikiApi( $wgServer .'/api.php' );
+$api->login( new \Mediawiki\Api\ApiUser( $REEVO_WIKI_API_USER, $REEVO_WIKI_API_PASS ) );
+$services = new \Mediawiki\Api\MediawikiFactory( $api );
+
+// Obtengo el nombre de la página como parámetro
+
+function ImportarImagenPrensa($pagetitle, $services) {
+ global $REEVO_URL;
+ $page = $services->newPageGetter()->getFromTitle( $pagetitle );
+ $pagecontent = $page->getRevisions()->getLatest();
+ if (!$pagecontent) {
+ echo "No existe una página con el nombre '$pagetitle'\n";
+ return;
+ }
+
+
+ $pagecontent = $page->getRevisions()->getLatest()->getContent()->getData();
+
+ // armo un array con las propiedades de SMW
+ $rows = explode("|", $pagecontent);
+ foreach($rows as $row => $data)
+ {
+ $row_data = explode('=', $data);
+ $page_properties[$row_data[0]] = $row_data[1];
+ }
+
+ if (array_key_exists('prensa:imagen', $page_properties)) {
+ echo "La página '$pagetitle' ya tiene una imagen\n";
+ return;
+ }
+
+ if (array_key_exists('prensa:elggid', $page_properties)) {
+ $elggidtmp = $page_properties['prensa:elggid'];
+ $elggid = preg_replace( "/\r|\n/", "", $elggidtmp );
+ // echo $url;
+ } else {
+ echo "La página '$pagetitle' no tiene definida la propiedad 'prensa:elggid'\n";
+ return;
+ }
+
+ // Definimos el nombre del archivo de la imagen
+ $pagename = str_replace( ":", "-", $pagetitle);
+ $snapshot_name = $pagename . '.jpg';
+ $snaphot_pagecontent = 'Imagen importada desde sitio anterior para [['. $pagetitle .']]';
+ $snaphot_desc = 'Imagen importada desde sitio anterior para [['. $pagetitle .']]';
+ $fileUploader = $services->newFileUploader();
+ $fileUploader->setChunkSize( 256 * 10 );
+ exec("cp /srv/reevo-web/files/red/recext/{$elggid}/`ls /srv/reevo-web/files/red/recext/{$elggid}/` /tmp/imagen && convert /tmp/imagen /tmp/imagen.jpg");
+ $fileUploader->upload( $snapshot_name, '/tmp/imagen.jpg', $snaphot_pagecontent, $snaphot_desc );
+
+ // Crea nueva o actualiza existente
+ $linkContent = str_replace('}}','|prensa:imagen='. $snapshot_name . '}}', $pagecontent);
+
+ $newContent = new \Mediawiki\DataModel\Content( $linkContent );
+ $title = new \Mediawiki\DataModel\Title( $pagetitle );
+ $identifier = new \Mediawiki\DataModel\PageIdentifier( $title );
+ $revision = new \Mediawiki\DataModel\Revision( $newContent, $identifier );
+ $services->newRevisionSaver()->save( $revision );
+ $link = urlencode("http://{$REEVO_URL}/Archivo:{$snapshot_name}");
+ echo "Importe la imagen para '{$pagetitle}' y se subio a: '{$link}'\n\n";
+}
+
+
+if ($argv[1]) {
+ ImportarImagenPrensa($argv[1],$services);
+} else {
+ echo "Al no indicar una página, voy a generar los snapshots de todas las pagínas que usen Plantilla:Prensa \n";
+
+ // Obtengo todas las paginas con Plantilla:Prensa
+ $pageListGetter = $services->newPageListGetter();
+ $examplePages = $pageListGetter->getPageListFromPageTransclusions( 'Template:Prensa' );
+ $array = accessProtected($examplePages,'pages');
+ foreach ( $array as $exPage ) {
+ $pagename = $exPage->getTitle()->getText();
+ echo "$pagename \n";
+ ImportarImagenPrensa($pagename, $services);
+ }
+}
+
+
+
+
+function accessProtected($obj, $prop) {
+ $reflection = new ReflectionClass($obj);
+ $property = $reflection->getProperty($prop);
+ $property->setAccessible(true);
+ return $property->getValue($obj);
+}
+
+
+
+
+
+// print_r($examplePages);
+
+// $myArray = json_decode(json_encode($examplePages), true);
+// print_r($examplePages->getPageList());
+
+
+
+
+
+
+?>
diff --git a/bin/wiki/ListarPaginas.php b/bin/wiki/ListarPaginas.php
new file mode 100644
index 00000000..96a4d6ef
--- /dev/null
+++ b/bin/wiki/ListarPaginas.php
@@ -0,0 +1,66 @@
+<?php
+# ----
+# Copyright (C) 2013-2020 - Reevo Project (http://reevo.org)
+# License: Affero GPL version 3 - http://www.gnu.org/licenses/agpl.html
+# ES: Este script recibe el nombre de una Plantilla o Categoría y lista todas las paginas que lo usan.
+# Se usa con: php ListarPaginas.php "Categoría:Experiencia"
+# ----
+
+
+// error_reporting(0);
+// Load all the stuff
+require_once( __DIR__ . '/vendor/autoload.php' );
+require_once( __DIR__ .'/../../etc/global_config.php' );
+
+// Log in to a wiki
+$api = new \Mediawiki\Api\MediawikiApi( $wgServer .'/api.php' );
+$api->login( new \Mediawiki\Api\ApiUser( $REEVO_WIKI_API_USER, $REEVO_WIKI_API_PASS ) );
+$services = new \Mediawiki\Api\MediawikiFactory( $api );
+
+// Obtengo el nombre del template como parámetro
+if ($argv[1]) {
+
+ $pageListGetter = $services->newPageListGetter();
+ $tipo = explode(':',$argv[1]);
+ switch ($tipo[0]) {
+ case 'Categoria':
+ $examplePages = $pageListGetter->getPageListFromCategoryName( $argv[1] );
+ break;
+ case 'Plantilla':
+ $examplePages = $pageListGetter->getPageListFromPageTransclusions( $argv[1] );
+ break;
+ default:
+ echo "No reconozco el tipo de objeto que estas intentando listar (debe ser Categoría o Plantilla)\n\n";
+ exit();
+ }
+ $array = accessProtected($examplePages,'pages');
+ foreach ( $array as $exPage ) {
+ $pagename = $exPage->getTitle()->getText();
+ echo "$pagename \n";
+ }
+} else {
+ echo "Hay que indicar una plantilla como parametro \n";
+}
+
+function accessProtected($obj, $prop) {
+ $reflection = new ReflectionClass($obj);
+ $property = $reflection->getProperty($prop);
+ $property->setAccessible(true);
+ return $property->getValue($obj);
+}
+
+
+
+
+
+// print_r($examplePages);
+
+// $myArray = json_decode(json_encode($examplePages), true);
+// print_r($examplePages->getPageList());
+
+
+
+
+
+
+?>
diff --git a/bin/wiki/PropiedadActualizar.php b/bin/wiki/PropiedadActualizar.php
new file mode 100644
index 00000000..81f845b5
--- /dev/null
+++ b/bin/wiki/PropiedadActualizar.php
@@ -0,0 +1,120 @@
+<?php
+# ----
+# Copyright (C) 2013-2020 - Reevo Project (http://reevo.org)
+# License: Affero GPL version 3 - http://www.gnu.org/licenses/agpl.html
+# ES: Este script recibe el nombre de una pagina e intenta agregar o actualizar las propiedades específicadas de las mismas con el valor dado.
+# Algo como: php PropiedadActualizar.php -t=Experiencia:Summerhill -f=true -p="experiencia:descripcion|Algo" -p="experiencia:lugar-pais|AR"
+# ----
+
+
+// error_reporting(0);
+// Load all the stuff
+require_once( __DIR__ . '/vendor/autoload.php' );
+require_once( __DIR__ .'/../../etc/global_config.php' );
+
+// Log in to a wiki
+$api = new \Mediawiki\Api\MediawikiApi( $wgServer .'/api.php' );
+$api->login( new \Mediawiki\Api\ApiUser( $REEVO_WIKI_API_USER, $REEVO_WIKI_API_PASS ) );
+$services = new \Mediawiki\Api\MediawikiFactory( $api );
+
+$opciones = getopt('t:p:f:');
+
+if ($opciones['t']) {
+ if ($opciones['p']) {
+ $title = $opciones['t'];
+ if (is_string($opciones['p'])) {
+ $prop[] = $opciones['p'];
+ } else {
+ $prop = $opciones['p'];
+ }
+ foreach ($prop as $key => $value) {
+ $p = explode('|', $value);
+ $propiedades[$p[0]] = $p[1];
+ }
+ $result = ActualizarPropiedades($title, $propiedades, $opciones, $services);
+ echo "\nSe actualizó correctamente la página $result ($wgServer/$result)\n\n\n";
+ } else {
+ echo "Es necesario que definas al menos una propiedad para agregar o modificar con -p='propiedad|valor'\n";
+ }
+} else {
+ echo "Es necesario que definas el nombe de la pagina a editar con -t='Pagina'\n";
+}
+
+// Obtengo el nombre de la página como parámetro
+
+function ActualizarPropiedades($title, $propiedades, $opciones, $services) {
+
+ $page = $services->newPageGetter()->getFromTitle( $title );
+ $pagecontent = $page->getRevisions()->getLatest();
+ if (!$pagecontent) {
+ echo "\n**** ERROR: No existe una página con el nombre '$title'\n\n\n";
+ exit();
+ } else {
+ echo "\n**** PROCESANDO: '$title' ****\n";
+ }
+
+ $pagecontent = $page->getRevisions()->getLatest()->getContent()->getData();
+
+ $template = preg_grep('/^\{{(.*(?<!}}))$/', explode("\n", $pagecontent));
+ $template_pos = array_key_first($template);
+ $templatefin = preg_grep('/^\}}(.*)$/', explode("\n", $pagecontent));
+ $templatefin_pos = array_key_first($templatefin);
+
+
+ $extracto = preg_grep('/^\|/', explode("\n", $pagecontent));
+ foreach ($extracto as $key => $value) {
+ $data = str_replace('|','',$value);
+ $data_limpia = explode('=', $data, 2);
+ $propiedades_previas[$data_limpia[0]] = $data_limpia[1];
+ }
+
+
+ // Verifico que tengo que hacer
+ if ($opciones['f'] == 'true') {
+ echo "\nVoy a actulizar y/o agregar las siguientes propiedades: \n";
+ $propiedades_actualizables = array_intersect_key($propiedades,$propiedades_previas);
+ print_r($propiedades_actualizables);
+ $propiedades_final = array_replace($propiedades_previas, $propiedades);
+ } else {
+ echo "\nNo se van a reemplazar los valores de propiedades existentes. Agrega '-f=true' para hacerlo. Propiedadedes existentes con sus valores actuales: \n";
+ $propiedades_actualizables = array_intersect_key($propiedades, $propiedades_previas);
+ print_r($propiedades_actualizables);
+
+ echo "\nPropiedades antes no definidas que voy a agregar: \n";
+ $propiedades_nuevas = array_diff_key($propiedades,$propiedades_previas);
+ print_r($propiedades_nuevas);
+ $propiedades_final = array_replace($propiedades_previas, $propiedades_nuevas);
+ }
+
+ // Construyo un array con el formato del template original
+ $propiedades_final_array[] = $template[$template_pos];
+ foreach ($propiedades_final as $key => $value) {
+ $propiedades_final_array[] = '|'.$key.'='.$value;
+ }
+ $propiedades_final_array[] = $templatefin[$templatefin_pos];
+
+ // Hago un array con el contenido de toda la pagina y reemplazo el template viejo por el nuevo
+ $contenido_array = explode("\n", $pagecontent);
+ $templatefin_pos = $templatefin_pos + 1;
+ array_splice($contenido_array, $template_pos, $templatefin_pos,$propiedades_final_array);
+
+ $contenido_final = implode("\n", $contenido_array);
+
+ $newContent = new \Mediawiki\DataModel\Content( $contenido_final );
+ $pagetitle = new \Mediawiki\DataModel\Title( $title );
+ $identifier = new \Mediawiki\DataModel\PageIdentifier( $pagetitle );
+ $revision = new \Mediawiki\DataModel\Revision( $newContent, $identifier );
+ $services->newRevisionSaver()->save( $revision );
+
+ return $title;
+
+}
+
+function accessProtected($obj, $prop) {
+ $reflection = new ReflectionClass($obj);
+ $property = $reflection->getProperty($prop);
+ $property->setAccessible(true);
+ return $property->getValue($obj);
+}
+
+?>
diff --git a/bin/wiki/PropiedadObtener.php b/bin/wiki/PropiedadObtener.php
new file mode 100644
index 00000000..114fba48
--- /dev/null
+++ b/bin/wiki/PropiedadObtener.php
@@ -0,0 +1,80 @@
+<?php
+# ----
+# Copyright (C) 2013-2020 - Reevo Project (http://reevo.org)
+# License: Affero GPL version 3 - http://www.gnu.org/licenses/agpl.html
+# ES: Este script recibe el nombre de una pagina e intenta obtener el valor las propiedades específicas.
+# Algo como: php PropiedadObtener.php -t=Experiencia:Summerhill -p="experiencia:lugar" -p="experiencia:lugar-pais"
+# ----
+
+
+// error_reporting(0);
+// Load all the stuff
+require_once( __DIR__ . '/vendor/autoload.php' );
+require_once( __DIR__ .'/../../etc/global_config.php' );
+
+// Log in to a wiki
+$api = new \Mediawiki\Api\MediawikiApi( $wgServer .'/api.php' );
+$api->login( new \Mediawiki\Api\ApiUser( $REEVO_WIKI_API_USER, $REEVO_WIKI_API_PASS ) );
+$services = new \Mediawiki\Api\MediawikiFactory( $api );
+
+$opciones = getopt('t:p:f:');
+
+if ($opciones['t']) {
+ if ($opciones['p']) {
+ $title = $opciones['t'];
+ if (is_string($opciones['p'])) {
+ $prop[] = $opciones['p'];
+ } else {
+ $prop = $opciones['p'];
+ }
+ foreach ($prop as $key => $value) {
+ // $p = explode('|', $value);
+ $propiedades[$value] = '';
+ }
+ $result = ObtenerPropiedades($title, $propiedades, $opciones, $services);
+ } else {
+ echo "Es necesario que definas al menos una propiedad para obtener con -p='propiedad|valor'\n";
+ }
+} else {
+ echo "Es necesario que definas el nombe de la pagina a revisar con -t='Pagina'\n";
+}
+
+// Obtengo el nombre de la página como parámetro
+
+function ObtenerPropiedades($title, $propiedades, $opciones, $services) {
+
+ $page = $services->newPageGetter()->getFromTitle( $title );
+ $pagecontent = $page->getRevisions()->getLatest();
+ if (!$pagecontent) {
+ echo "\n**** ERROR: No existe una página con el nombre '$title'\n\n\n";
+ exit();
+ } else {
+ // echo "\n**** PROCESANDO: '$title' ****\n";
+ }
+
+ $pagecontent = $page->getRevisions()->getLatest()->getContent()->getData();
+
+ // armo un array con las propiedades de SMW
+ $extracto = preg_grep('/^\|/', explode("\n", $pagecontent));
+ foreach ($extracto as $key => $value) {
+ $data = str_replace('|','',$value);
+ $data_limpia = explode('=', $data);
+ $propiedades_previas[$data_limpia[0]] = $data_limpia[1];
+ }
+
+ foreach ($propiedades as $key => $value) {
+ if (array_key_exists($key, $propiedades_previas)) {
+ echo "$propiedades_previas[$key]\n";
+ }
+ }
+
+}
+
+function accessProtected($obj, $prop) {
+ $reflection = new ReflectionClass($obj);
+ $property = $reflection->getProperty($prop);
+ $property->setAccessible(true);
+ return $property->getValue($obj);
+}
+
+?>
diff --git a/bin/wiki/composer.json b/bin/wiki/composer.json
new file mode 100644
index 00000000..62491e7e
--- /dev/null
+++ b/bin/wiki/composer.json
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "addwiki/mediawiki-api": "~0.7.0"
+ }
+}
diff --git a/bin/wiki/composer.lock b/bin/wiki/composer.lock
new file mode 100644
index 00000000..0a32d572
--- /dev/null
+++ b/bin/wiki/composer.lock
@@ -0,0 +1,487 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "1dfde0453a3f59ee7cce361eee3cbcdf",
+ "packages": [
+ {
+ "name": "addwiki/mediawiki-api",
+ "version": "0.7.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/addwiki/mediawiki-api.git",
+ "reference": "f52fc3760d82774512d344e41c45c878a2c6659e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/addwiki/mediawiki-api/zipball/f52fc3760d82774512d344e41c45c878a2c6659e",
+ "reference": "f52fc3760d82774512d344e41c45c878a2c6659e",
+ "shasum": ""
+ },
+ "require": {
+ "addwiki/mediawiki-api-base": "~2.4",
+ "addwiki/mediawiki-datamodel": "~0.7.0"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "^0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "monolog/monolog": "^1.23",
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "description": "A MediaWiki API library",
+ "keywords": [
+ "mediawiki"
+ ],
+ "time": "2017-11-20T03:08:06+00:00"
+ },
+ {
+ "name": "addwiki/mediawiki-api-base",
+ "version": "2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/addwiki/mediawiki-api-base.git",
+ "reference": "33c147e91d05a48e953839fb3ad9e6386cfd85c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/addwiki/mediawiki-api-base/zipball/33c147e91d05a48e953839fb3ad9e6386cfd85c1",
+ "reference": "33c147e91d05a48e953839fb3ad9e6386cfd85c1",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "~6.0",
+ "guzzlehttp/promises": "~1.0",
+ "php": ">=5.5",
+ "psr/log": "~1.0"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "phpunit/phpunit": "~4.8.0|~5.3.0"
+ },
+ "suggest": {
+ "etsy/phan": "Allows running static analysis on the package (requires PHP 7+)"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "description": "A basic Mediawiki api base library",
+ "keywords": [
+ "mediawiki"
+ ],
+ "time": "2017-11-02T10:53:36+00:00"
+ },
+ {
+ "name": "addwiki/mediawiki-datamodel",
+ "version": "0.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/addwiki/mediawiki-datamodel.git",
+ "reference": "05dd783715a92ec5449bab4091c0482cf3fcface"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/addwiki/mediawiki-datamodel/zipball/05dd783715a92ec5449bab4091c0482cf3fcface",
+ "reference": "05dd783715a92ec5449bab4091c0482cf3fcface",
+ "shasum": ""
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "phpunit/phpunit": "~4.8.0|~5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\DataModel\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "description": "A Mediawiki datamodel",
+ "keywords": [
+ "mediawiki"
+ ],
+ "time": "2018-01-10T19:14:13+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "6.3.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+ "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/promises": "^1.0",
+ "guzzlehttp/psr7": "^1.4",
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+ "psr/log": "^1.0"
+ },
+ "suggest": {
+ "psr/log": "Required for using the Log middleware"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.3-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "time": "2018-04-22T15:46:56+00:00"
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "v1.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+ "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ],
+ "time": "2016-12-20T10:07:11+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "9f83dded91781a01c63574e387eaa769be769115"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
+ "reference": "9f83dded91781a01c63574e387eaa769be769115",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "time": "2018-12-04T20:46:45+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "time": "2018-11-20T15:27:04+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "2.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+ "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~3.7.0",
+ "satooshi/php-coveralls": ">=1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "time": "2016-02-11T07:05:27+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/bin/wiki/initReevo.php b/bin/wiki/initReevo.php
new file mode 100644
index 00000000..81a006b0
--- /dev/null
+++ b/bin/wiki/initReevo.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * scritp to initialise specifics wikifab pages, such as forms, properties, and home page
+ *
+ * @file
+ * @ingroup Maintenance
+ */
+require_once __DIR__ . '/initReevoClass.php';
+
+$maintClass = "InitReevo";
+require_once RUN_MAINTENANCE_IF_MAIN;
diff --git a/bin/wiki/initReevoClass.php b/bin/wiki/initReevoClass.php
new file mode 100644
index 00000000..ee67ded7
--- /dev/null
+++ b/bin/wiki/initReevoClass.php
@@ -0,0 +1,210 @@
+<?php
+# ----
+# Copyright (C) 2013-2020 - Reevo Project (http://reevo.org)
+# License: Affero GPL version 3 - http://www.gnu.org/licenses/agpl.html
+# ES: Script usado para importar páginas de REEVO como formularios, propiedades, etc.
+# Creditos: Basado en WikiFab: https://github.com/Wikifab/wikifab-main/tree/master/maintenance
+# ----
+
+require_once '../../www/wiki/maintenance/Maintenance.php';
+
+class InitReevo extends Maintenance {
+
+ public function __construct() {
+ parent::__construct ();
+ $this->mDescription = "Init Reevo pages";
+ $this->addArg( 'namespace', 'El namspace especifico del cual generar las pagínas. Tambien puede pasarse un nombre de página especifico, ej: Portada.mw', false );
+ $this->addOption ( 'folder', 'Importa las paginas de las carpeta especificada', false , false);
+ $this->addOption ( 'setReevoHomePage', "Cambia la portada", false, false );
+ $this->addOption ( 'force', "Fuerza la edición cuando las páginas ya existan", false, false );
+ $this->addOption ( 'int', "Usar solo páginas internacionales, de la carpeta 'int'", false, false );
+ }
+
+ protected function getUpdateKey() {
+ return 'initialise_Reevo_Pages';
+ }
+
+ protected function updateSkippedMessage() {
+ return 'Reevo pages are allready setup';
+ }
+
+ public function execute() {
+ global $wgContLang;
+
+ $setReevoHomePage = $this->getOption ( 'setReevoHomePage' );
+ $force = $this->getOption ( 'force' ) ? true : false;
+
+ if ($this->getOption ( 'folder' )) {
+ echo 'Voy a importar de la carpeta: '. $this->getOption ( 'folder' );
+ $lang = $this->getOption ( 'folder' );
+ } else {
+ $lang = $this->getOption ( 'int' ) ? 'int' : $wgContLang->getCode();
+ }
+
+
+ $homePageFile = [
+ 'es' => 'Portada.md',
+ 'en' => 'Main_Page.md',
+ 'int' => 'Portada.md'
+ ];
+
+ $pagelist = $this->getPageListToCreate ($lang);
+
+ echo "Setting Up Reevo pages ...\n";
+
+
+ if ($setReevoHomePage) {
+ echo "Setting wiki home page $setReevoHomePage\n";
+
+ $ret = Title::newMainPage();
+ $pageTitle = $ret->getText();
+ $title = $this->getPageName ( $pageTitle );
+ $content = $this->getPageContent ( $homePageFile[$lang], $lang);
+ $this->createPage ( $title, $content, true);
+
+ } else {
+ echo "No Setting wiki home page\n";
+ }
+
+ foreach ( $pagelist as $page ) {
+ if ($page == $homePageFile) {
+ continue;
+ }
+ $title = $this->getPageName ( $page );
+ $content = $this->getPageContent ($page, $lang);
+ $this->createPage ( $title, $content , $force);
+
+ $t = Title::newFromText( $title );
+ $protection = "sysop";
+ $user = User::newSystemUser( 'Admin', [ 'steal' => true ] );
+ $cascade = '';
+ $reason = 'Se protege por seguridad';
+
+ $restrictions = [];
+ foreach ( $t->getRestrictionTypes() as $type ) {
+ $restrictions[$type] = $protection;
+ }
+
+ $page = WikiPage::factory( $t );
+ $status = $page->doUpdateRestrictions( $restrictions, [], $cascade, $reason, $user );
+ }
+ }
+
+ /**
+ * Get a WikiPage object from a title string, if possible.
+ *
+ * @param string $titleName
+ * @param bool|string $load
+ * Whether load the object's state from the database:
+ * - false: don't load (if the pageid is given, it will still be loaded)
+ * - 'fromdb': load from a slave database
+ * - 'fromdbmaster': load from the master database
+ * @return WikiPage
+ */
+ protected function getPage($titleName) {
+ $titleObj = Title::newFromText ( $titleName );
+ if (! $titleObj || $titleObj->isExternal ()) {
+ trigger_error ( 'Fail to get title ' . $titleName, E_USER_WARNI );
+ return false;
+ }
+ if (! $titleObj->canExist ()) {
+ trigger_error ( 'Title cannot be created ' . $titleName, E_USER_WARNING );
+ return false;
+ }
+ $pageObj = WikiPage::factory ( $titleObj );
+
+ return $pageObj;
+ }
+ protected function getAdminUser() {
+ // get Admin user : (take the first user created)
+ $dbr = wfGetDB ( DB_SLAVE );
+ $res = $dbr->select ( 'user', User::selectFields (), array (), __METHOD__, array (
+ 'LIMIT' => 1,
+ 'ORDER BY' => 'user_id'
+ ) );
+ $users = UserArray::newFromResult ( $res );
+ $user = $users->current ();
+
+ return $user;
+ }
+ protected function createPage($pageName, $text, $force = false) {
+ $wikipage = $this->getPage ( $pageName );
+
+ if ($wikipage->exists () && ! $force) {
+ echo "page $pageName allready exists.\n";
+ return false;
+ }
+
+ $user = $this->getAdminUser ();
+
+ $content = ContentHandler::makeContent( $text, $wikipage->getTitle() );
+ $result = $wikipage->doEditContent( $content, 'Actualizada automaticamente con versión de repositorio', $flags = 0, $baseRevId = false, $user );
+
+ if ($result->isOK ()) {
+ echo "page $pageName successfully created.\n";
+ return true;
+ } else {
+ echo $result->getWikiText ();
+ }
+
+ return false;
+ }
+ protected function getPageName($page) {
+ // $page = str_replace ( 'Formulario_', 'Formulario:', $page );
+ // $page = str_replace ( 'Propiedad_', 'Propiedad:', $page );
+ // if (strpos($page,'Template_') == 0) {
+ // $page = str_replace ( 'Plantilla_', 'Plantilla:', $page );
+ // }
+ // $page = str_replace ( 'Modulo_', 'Modulo:', $page );
+ // $page = str_replace ( 'Categoría_', 'Categoría:', $page );
+ // $page = str_replace ( 'MediaWiki_', 'Mediawiki:', $page );
+ // $page = str_replace ( 'Widget_', 'Widget:', $page );
+ // $page = str_replace ( 'Ayuda_', 'Ayuda:', $page );
+ // $page = str_replace ( 'Reevo_', 'Reevo:', $page );
+ $page = str_replace ( '_', ' ', $page );
+ $page = str_replace ( '~', '/', $page );
+ $page = str_replace ( '.mw', '', $page );
+
+ return $page;
+ }
+ protected function getPagesDirs($lang) {
+ return [
+ __DIR__ . '/reevoPages/' . $lang
+ ];
+ }
+ protected function getPageContent($page, $lang = 'int') {
+ $dirs = $this->getPagesDirs($lang);
+
+ foreach ($dirs as $dir) {
+ if (file_exists($dir . '/' . $page)) {
+ return file_get_contents ( $dir . '/' . $page );
+ }
+ }
+
+ throw new Exception('File not found : ' . $page);
+ }
+ protected function getPageListToCreate( $lang) {
+
+ $result = [ ];
+
+ $dirs = $this->getPagesDirs($lang);
+ foreach ($dirs as $dir) {
+ $files = scandir ( $dir );
+ foreach ( $files as $file ) {
+ if (preg_match ( '/^([a-zA-Z_0-9\-áéíóúñÁÉÍÓÚÑ~:’()])+\.mw$/', $file )) {
+ $namespace = $this->getArg( 0 );
+ if ($namespace) {
+ if(strpos($file, $namespace) === 0) {
+ // echo "$file\n";
+ $result[$file] = $file;
+ }
+ } else {
+ $result[$file] = $file;
+ }
+ }
+ }
+ }
+ print_r($result);
+ return $result;
+ }
+}
diff --git a/bin/wiki/reevoPages/int/Formulario:Audiovisual.mw b/bin/wiki/reevoPages/int/Formulario:Audiovisual.mw
new file mode 100644
index 00000000..624bda1a
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Formulario:Audiovisual.mw
@@ -0,0 +1,90 @@
+<noinclude>
+Este es el formulario "Audiovisual".
+Para crear una página con este formulario, escribe el nombre de la página a continuación;
+si ya existe una página con ese nombre, serás dirigido a un formulario para editar esa página.
+
+
+{{#forminput:form=Audiovisual|query string=namespace=Audiovisual|autocomplete on namespace=Audiovisual}}
+
+</noinclude><includeonly>
+<div id="wikiPreview" style="display: none; padding-bottom: 25px; margin-bottom: 25px; border-bottom: 1px solid #AAAAAA;"></div>
+{{{for template|audiovisual}}}
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:titulo}}</div>
+<div class="col-sm-10">{{{field|audiovisual:titulo|input type=text|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:estreno}}</div>
+<div class="col-sm-10">{{{field|audiovisual:estreno|input type=year|mandatory|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-form-label col-sm-2 pt-0">{{int:rv-audiovisual:pais}}</div>
+<div class="col-sm-10">{{{field|audiovisual:pais|class=form-control|input type=dropdown|mandatory|mapping using translate=rv-pais-}}}
+</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">Estado</div>
+<div class="col-sm-10">{{{field|Estado|input type=SF_Select|function=states:@@@@|sametemplate|field=audiovisual:pais|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-form-label col-sm-2 pt-0">{{int:rv-audiovisual:genero}}</div>
+<div class="col-sm-10">{{{field|audiovisual:genero|input type=radiobutton|class=radio-inline|mandatory|default=documental|mapping using translate=rv-audiovisual:genero:}}}
+</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:duracion}}</div>
+<div class="col-sm-10">{{{field|audiovisual:duracion|mandatory|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:autoria}}</div>
+<div class="col-sm-10">{{{field|audiovisual:autoria|input type=text|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:web}}</div>
+<div class="col-sm-10">{{{field|audiovisual:web|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:urltrailer}}</div>
+<div class="col-sm-10">{{{field|audiovisual:urltrailer|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:url}}</div>
+<div class="col-sm-10">{{{field|audiovisual:url|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:magnet}}</div>
+<div class="col-sm-10">{{{field|audiovisual:magnet|class=form-control}}}</div>
+</div>
+
+<div class="form-group">
+<div class="row">
+<div class="col-form-label col-sm-2 pt-0">{{int:rv-audiovisual:sub}}</div>
+<div class="col-sm-10">{{{field|audiovisual:sub|input type=checkboxes|class=radio-inline|mapping using translate=rv-audiovisual:sub:}}}</div>
+</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-audiovisual:poster}}</div>
+<div class="col-sm-10">{{{field|audiovisual:poster|input type=text|class=form-control|uploadable|image preview|default filename=audiovisual-poster-{{PAGENAME}}.jpg}}}</div>
+</div>
+{{{end template}}}
+
+
+{{int:rv-form:label:content}}
+
+{{{standard input|free text|rows=10|autogrow|editor=wikieditor}}}
+
+{{{standard input|save}}} {{{standard input|preview}}} {{{standard input|changes}}} {{{standard input|cancel}}}
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Formulario:Experiencia.mw b/bin/wiki/reevoPages/int/Formulario:Experiencia.mw
new file mode 100644
index 00000000..012ac493
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Formulario:Experiencia.mw
@@ -0,0 +1,181 @@
+<noinclude>
+Este es el formulario "Experiencia".
+Para crear una página con este formulario, escribe el nombre de la página a continuación;
+si ya existe una página con ese nombre, serás dirigido a un formulario para editar esa página.
+
+
+{{#forminput:form=Experiencia|query string=namespace=Experiencia|autocomplete on namespace=Experiencia}}
+
+</noinclude><includeonly>
+<div id="wikiPreview" style="display: none; padding-bottom: 25px; margin-bottom: 25px; border-bottom: 1px solid #AAAAAA;"></div>
+{{{for template|Experiencia}}}
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:descripcion}}{{#info:{{int:rv-experiencia:descripcion:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:descripcion|input type=textarea|class=form-control}}}</div>
+</div>
+
+<hr/>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:contacto-url}}{{#info:{{int:rv-experiencia:contacto-url:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:contacto-url|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:contacto-email}}{{#info:{{int:rv-experiencia:contacto-email:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:contacto-email|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:contacto-telefono}}{{#info:{{int:rv-experiencia:contacto-telefono:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:contacto-telefono|class=form-control}}}</div>
+</div>
+
+<hr/>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:lugar-pais}}{{#info:{{int:rv-experiencia:lugar-pais:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:lugar-pais|class=form-control|input type=dropdown|mandatory|mapping using translate=rv-pais-|feeds to p=Experiencia[experiencia:lugar]}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:lugar-provincia}}{{#info:{{int:rv-experiencia:lugar-provincia:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:lugar-provincia|input type=SF_Select|function=states:@@@@|sametemplate|field=experiencia:lugar-pais|class=form-control|feeds to map=Experiencia[experiencia:lugar]}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:lugar-ciudad}}{{#info:{{int:rv-experiencia:lugar-ciudad:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:lugar-ciudad|input type=text|feeds to map=Experiencia[experiencia:lugar]|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:lugar-calle}}{{#info:{{int:rv-experiencia:lugar-calle:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:lugar-calle|input type=text|feeds to map=Experiencia[experiencia:lugar]|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:lugar}}{{#info:{{int:rv-experiencia:lugar:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:lugar|input type=googlemaps|width=100%|class=form-control}}}</div>
+</div>
+
+
+<hr/>
+<h2>Datos de organización y gestión</h2>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-inicio}}{{#info:{{int:rv-experiencia:info-inicio:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-inicio|input type=datepicker|class=form-control|week start=1|highlight days of week=0|date format=dd-mm-yy}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-final}}{{#info:{{int:rv-experiencia:info-final:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-final|input type=datepicker|class=form-control|week start=1|highlight days of week=0|date format=dd-mm-yy}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-organizacion}}{{#info:{{int:rv-experiencia:info-organizacion:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-organizacion|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-gestion}}{{#info:{{int:rv-experiencia:info-gestion:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-gestion|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+<hr/>
+<h2>Datos demográficos</h2>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-nivel}}{{#info:{{int:rv-experiencia:info-nivel:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-nivel|input type=checkboxes|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-niveleconomico}}{{#info:{{int:rv-experiencia:info-niveleconomico:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-niveleconomico|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-contextosocial}}{{#info:{{int:rv-experiencia:info-contextosocial:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-contextosocial|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-participantes}}{{#info:{{int:rv-experiencia:info-participantes:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-participantes|class=form-control|input type=regexp|message={{int:rv-form:label:valid:integer}}|regexp=/^[0-9 ]+$/}}}</div>
+</div>
+
+
+<hr/>
+<h2>Aspectos educativos y pedagógicos</h2>
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-corrientes}}{{#info:{{int:rv-experiencia:info-corrientes:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-corrientes|input type=tree|top category=Corrientes pedagógicas|list|hideroot}}}</div>
+</div>
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-enfoques}}{{#info:{{int:rv-experiencia:info-enfoques:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-enfoques|input type=tree|top category=Enfoques temáticos|list|hideroot}}}</div>
+</div>
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-tipoeducacion}}{{#info:{{int:rv-experiencia:info-tipoeducacion:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-tipoeducacion|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-certificacion}}{{#info:{{int:rv-experiencia:info-certificacion:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-certificacion|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-virtual}}{{#info:{{int:rv-experiencia:info-virtual:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-virtual|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+<hr/>
+<h2>Aspectos económicos</h2>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-arancel}}{{#info:{{int:rv-experiencia:info-arancel:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-arancel|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-lucro}}{{#info:{{int:rv-experiencia:info-lucro:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-lucro|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-experiencia:info-voluntariado}}{{#info:{{int:rv-experiencia:info-voluntariado:ayuda}}}}</div>
+<div class="col-sm-10">{{{field|experiencia:info-voluntariado|input type=dropbox|class=form-control|mapping using translate=rv-experiencia:respuesta-}}}</div>
+</div>
+
+
+
+
+{{{end template}}}
+
+'''Texto libre:'''
+
+{{{standard input|free text|rows=10|editor=wikieditor}}}
+
+
+{{{standard input|summary}}}
+
+{{{standard input|minor edit}}} {{{standard input|watch}}}
+
+{{{standard input|save}}} {{{standard input|preview}}} {{{standard input|changes}}} {{{standard input|cancel}}}
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Formulario:Prensa.mw b/bin/wiki/reevoPages/int/Formulario:Prensa.mw
new file mode 100644
index 00000000..39b8d356
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Formulario:Prensa.mw
@@ -0,0 +1,64 @@
+<noinclude>
+Este es el formulario "Prensa".
+Para crear una página con este formulario, escribe el nombre de la página a continuación;
+si ya existe una página con ese nombre, serás dirigido a un formulario para editar esa página.
+
+
+{{#forminput:form=Prensa|query string=namespace=Prensa|autocomplete on namespace=Prensa}}
+
+</noinclude><includeonly>
+<div id="wikiPreview" style="display: none; padding-bottom: 25px; margin-bottom: 25px; border-bottom: 1px solid #AAAAAA;"></div>
+{{{for template|Prensa}}}
+{| class="formtable"
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-prensa:url}}</div>
+<div class="col-sm-10">{{{field|prensa:url|unique|=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-prensa:fecha}}</div>
+<div class="col-sm-10">{{{field|prensa:fecha|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-prensa:fuente}}</div>
+<div class="col-sm-10">{{{field|prensa:fuente|class=form-control|values from property=prensa:fuente}}}</div>
+</div>
+
+<div class="form-group">
+<div class="row">
+<div class="col-form-label col-sm-2 pt-0">{{int:rv-prensa:pais}}</div>
+<div class="col-sm-10">{{{field|prensa:pais|class=form-control|input type=dropdown|mandatory|mapping using translate=rv-pais-}}}
+</div>
+</div>
+</div>
+
+<div class="form-group">
+<div class="row">
+<div class="col-form-label col-sm-2 pt-0">{{int:rv-prensa:idioma}}</div>
+<div class="col-sm-10">{{{field|prensa:idioma|input type=dropdown|class=radio-inline|mapping using translate=rv-prensa:idioma:}}}</div>
+</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-prensa:descripcion}}</div>
+<div class="col-sm-10">{{{field|prensa:descripcion|input type=textarea|class=form-control}}}</div>
+</div>
+
+<div class="form-group row">
+<div class="col-sm-2 col-form-label">{{int:rv-prensa:imagen}}</div>
+<div class="col-sm-10">{{{field|prensa:imagen|input type=text|class=form-control|uploadable|image preview|default filename=Prensa-{{PAGENAME}}.jpg}}}</div>
+</div>
+
+
+{{{end template}}}
+
+
+
+{{{standard input|summary}}}
+
+{{{standard input|minor edit}}} {{{standard input|watch}}}
+
+{{{standard input|save}}} {{{standard input|preview}}} {{{standard input|changes}}} {{{standard input|cancel}}}
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/MediaWiki:Mainpage.mw b/bin/wiki/reevoPages/int/MediaWiki:Mainpage.mw
new file mode 100644
index 00000000..f858d150
--- /dev/null
+++ b/bin/wiki/reevoPages/int/MediaWiki:Mainpage.mw
@@ -0,0 +1 @@
+Portada
diff --git a/bin/wiki/reevoPages/int/MediaWiki:Sidebar.mw b/bin/wiki/reevoPages/int/MediaWiki:Sidebar.mw
new file mode 100644
index 00000000..adb7d148
--- /dev/null
+++ b/bin/wiki/reevoPages/int/MediaWiki:Sidebar.mw
@@ -0,0 +1,15 @@
+* navigation
+** Nosotrxs|{{int:rv-menu:nosotrxs}}
+** Participar|{{int:rv-menu:participar}}
+** Donar|{{int:rv-menu:donar}}
+* rv-menu:ayuda
+** Ayuda:Cómo se edita una página|{{int:rv-menu:ayuda:editar}}
+** recentchanges-url|recentchanges
+* rv-menu:contenidos
+** Experiencia|{{int:rv-menu:contenidos:experiencia}}
+** Prensa|{{int:rv-menu:contenidos:prensa}}
+** Audiovisual|{{int:rv-menu:contenidos:audiovisual}}
+** Bibliografía|{{int:rv-menu:contenidos:bibliografia}}
+** Evento|{{int:rv-menu:contenidos:evento}}
+** ----
+** Especial:FormStart|{{int:rv-menu:sumar}}
diff --git a/bin/wiki/reevoPages/int/Plantilla:Audiovisual.mw b/bin/wiki/reevoPages/int/Plantilla:Audiovisual.mw
new file mode 100644
index 00000000..d36aec01
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Audiovisual.mw
@@ -0,0 +1,72 @@
+<noinclude>
+<pre>
+{{Audiovisual
+|audiovisual:titulo=
+|audiovisual:estreno=
+|audiovisual:pais=
+|audiovisual:duracion=
+|audiovisual:autoria=
+|audiovisual:genero=
+|audiovisual:web=
+|audiovisual:urltrailer=
+|audiovisual:url=
+|audiovisual:magnet=
+|audiovisual:sub=
+|audiovisual:poster=
+}}
+</pre>
+</noinclude>
+<includeonly><div class='col-lg-4 col-md-6 col-sm-12 col-xs-12'>
+{| class="wikitable"
+|+{{#if: {{{audiovisual:poster|}}} | [[Archivo:{{#setmainimage:{{{audiovisual:poster|}}}}}]] }}
+|-
+! {{int:rv-audiovisual:titulo}}
+| [[Audiovisual:titulo::{{{audiovisual:titulo|}}}]]
+|-
+! {{int:rv-audiovisual:estreno}}
+| [[Audiovisual:estreno::{{{audiovisual:estreno|}}}]] [{{SERVER}}/index.php?title=Especial%3ABuscarPorPropiedad&property=Audiovisual%3Aestreno&value={{{audiovisual:estreno|}}} +]
+|-
+! {{int:rv-audiovisual:pais}}
+| {{int:rv-pais-{{{audiovisual:pais|}}}}}<span style="display:none">[[Audiovisual:pais::{{{audiovisual:pais|}}}]]</span> [{{SERVER}}/index.php?title=Especial%3ABuscarPorPropiedad&property=Audiovisual%3Apais&value={{{audiovisual:pais|}}} +]
+|-
+! {{int:rv-audiovisual:duracion}}
+| [[Audiovisual:duracion::{{{audiovisual:duracion|}}}]]
+|-
+! {{int:rv-audiovisual:autoria}}
+| [[Audiovisual:autoria::{{{audiovisual:autoria|}}}]]
+|-
+! {{int:rv-audiovisual:genero}}
+| {{int:rv-audiovisual:genero:{{{audiovisual:genero|}}}}} [{{SERVER}}/index.php?title=Especial%3ABuscarPorPropiedad&property=Audiovisual%3Agenero&value={{{audiovisual:genero|}}} +]
+|-
+! {{int:rv-audiovisual:web}}
+| [[Audiovisual:web::{{{audiovisual:web|}}}]]
+|-
+! {{int:rv-audiovisual:sub}}
+| [[Audiovisual:sub::{{{audiovisual:sub|}}}]]
+|}</div><div class='col-lg-8 col-md-6 col-sm-12 col-xs-12'>{{#if:{{{audiovisual:urltrailer|}}} | == {{int:rv-audiovisual:seccion:trailer}} ==
+{{#evu:{{{audiovisual:urltrailer|}}}}}
+}}
+
+{{#if: {{{audiovisual:url|}}} |
+== {{int:rv-audiovisual:seccion:online}} ==
+{{#evu:{{{audiovisual:url|}}}}}
+}}
+
+{{#if: {{#rmatch:{{#USERNAME:}}|/(.*)\.(.*)\.(.*)\.(.*)/||{{#USERNAME:}}}} |
+{{#if: {{{audiovisual:magnet|}}} |
+== {{int:rv-audiovisual:seccion:descarga}} ==
+{{{audiovisual:magnet|}}}
+}}
+}}
+</div>
+
+[[Categoría:Audiovisual]]
+
+<div style='display:none;'>
+[[Audiovisual:poster::{{{audiovisual:poster|}}}]]
+[[Audiovisual:genero::{{{audiovisual:genero|}}}]]
+[[Audiovisual:url::{{{audiovisual:url|}}}]]
+[[Audiovisual:urltrailer::{{{audiovisual:urltrailer|}}}]]
+[[Audiovisual:magnet::{{{audiovisual:magnet|}}}]]
+</div>
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Experiencia.mw b/bin/wiki/reevoPages/int/Plantilla:Experiencia.mw
new file mode 100644
index 00000000..d35f59db
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Experiencia.mw
@@ -0,0 +1,131 @@
+<noinclude>
+Esta es la plantilla "Experiencia". Debe llamarse en el siguiente formato:
+<pre>
+{{Experiencia
+|experiencia:descripcion=
+
+|experiencia:contacto-url=
+|experiencia:contacto-email=
+|experiencia:contacto-telefono=
+
+|experiencia:imagen-destacada=
+
+|experiencia:lugar=
+|experiencia:lugar-pais=
+|experiencia:lugar-provincia=
+|experiencia:lugar-ciudad=
+|experiencia:lugar-calle=
+
+|experiencia:info-inicio=
+|experiencia:info-final=
+|experiencia:info-niveleconomico=
+|experiencia:info-contextosocial=
+|experiencia:info-participantes=
+|experiencia:info-nivel=
+|experiencia:info-organizacion=
+|experiencia:info-gestion=
+|experiencia:info-voluntariado=
+
+|experiencia:info-corrientes=
+|experiencia:info-enfoques=
+
+|experiencia:info-certificacion=
+|experiencia:info-tipoeducacion=
+|experiencia:info-arancel=
+|experiencia:info-lucro=
+|experiencia:info-virtual=
+
+# Parametros que no son propiedades semanticas
+
+|experiencia:oldid=
+|experiencia:imagen-destacada-zoom=
+|banner-altura=
+}}
+</pre>
+Editar la página para ver el texto de la plantilla.
+</noinclude><includeonly>{{#if: {{{experiencia:info-final|}}} |
+<div class="alert alert-warning">
+{{int:rv-experiencia:info-final-nota}}[[Categoría:Experiencias finalizadas]]
+</div>
+}}
+<div class="intro-copete">
+[[Experiencia:descripcion::{{{experiencia:descripcion|}}}]]{{#description2:{{{experiencia:descripcion|}}}}}
+</div>
+<div class="intro-links">
+{{#if: {{{experiencia:contacto-url|}}} | <span id="intro-links-url"><span class="glyphicon glyphicon-home"></span> [{{{experiencia:contacto-url|}}} Sitio web] </span>}}{{#if: {{{experiencia:contacto-email|}}} | <nowiki> | </nowiki> <span id="intro-links-email"><span class="glyphicon glyphicon-envelope"></span> [[Experiencia:contacto-email::{{{experiencia:contacto-email|}}}]] </span>}}{{#if: {{{experiencia:contacto-telefono|}}} | <nowiki> | </nowiki> <span id="intro-links-telefono"><span class="glyphicon glyphicon-earphone"></span> [[Experiencia:contacto-telefono::{{{experiencia:contacto-telefono|}}}]] </span>}}
+</div><div style='padding-left: 0 !important;' class='col-lg-4 col-md-6 col-sm-12 col-xs-12'>
+{| class="wikitable"
+|+ {{#if: {{{experiencia:lugar|}}} | {{#display_map:{{{experiencia:lugar|}}}|width=100%|enablefullscreen=yes|height=300|service=leaflet}} }}<span class="map-address">{{#if: {{{experiencia:lugar-calle|}}} | [[Experiencia:lugar-calle::{{{experiencia:lugar-calle|}}}]] }}, {{#if: {{{experiencia:lugar-ciudad|}}} | [[Experiencia:lugar-ciudad::{{{experiencia:lugar-ciudad|}}}]] }}, {{#if: {{{experiencia:lugar-provincia|}}} | [[Experiencia:lugar-provincia::{{{experiencia:lugar-provincia|}}}]] }}, {{#if: {{{experiencia:lugar-pais|}}} | {{int:rv-pais-{{{experiencia:lugar-pais|}}}}} <span style="display:none">[[Experiencia:lugar-pais::{{{experiencia:lugar-pais|}}}]]</span> }}</span>
+|- {{#if: {{{experiencia:info-inicio|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-inicio}}
+| [[Experiencia:info-inicio::{{{experiencia:info-inicio|}}}]]
+|- {{#if: {{{experiencia:info-final|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-final}}
+| [[Experiencia:info-final::{{{experiencia:info-final|}}}]]
+|- {{#if: {{{experiencia:info-corrientes|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-corrientes}}
+| {{#arraymap:{{{experiencia:info-corrientes|}}}|,|@|[[:Categoría:@|@]] |<nowiki>, </nowiki>}}<span style='display:none;'>[[Experiencia:info-corrientes::{{{experiencia:info-corrientes|}}}]]</span>
+|- {{#if: {{{experiencia:info-enfoques|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-enfoques}}
+| {{#arraymap:{{{experiencia:info-enfoques|}}}|,|@|[[:Categoría:@|@]] |<nowiki>, </nowiki>}}<span style='display:none;'>[[Experiencia:info-corrientes::{{{experiencia:info-corrientes|}}}]]</span>
+|- {{#if: {{{experiencia:info-niveleconomico|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-niveleconomico}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-niveleconomico|}}}}}
+|- {{#if: {{{experiencia:info-contextosocial|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-contextosocial}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-contextosocial|}}}}}
+|- {{#if: {{{experiencia:info-participantes|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-participantes}}
+| {{{experiencia:info-participantes|}}}
+|- {{#if: {{{experiencia:info-nivel|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-nivel}}
+| {{#arraymap:{{{experiencia:info-nivel|}}}|,|@|{{int:rv-experiencia:respuesta-@}} |<nowiki>, </nowiki>}}<span style='display:none;'>[[Experiencia:info-nivel::{{{experiencia:info-nivel|}}}]]</span>
+|- {{#if: {{{experiencia:info-organizacion|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-organizacion}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-organizacion|}}}}}
+|- {{#if: {{{experiencia:info-gestion|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-gestion}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-gestion|}}}}}
+|- {{#if: {{{experiencia:info-tipoeducacion|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-tipoeducacion}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-tipoeducacion|}}}}}
+|- {{#if: {{{experiencia:info-certificacion|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-certificacion}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-certificacion|}}}}}
+|- {{#if: {{{experiencia:info-lucro|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-arancel}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-arancel|}}}}}
+|- {{#if: {{{experiencia:info-lucro|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-lucro}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-lucro|}}}}}
+|- {{#if: {{{experiencia:info-voluntariadol|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-voluntariadol}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-virtual|}}}}}
+|- {{#if: {{{experiencia:info-virtual|}}} | | style="display:none;" }}
+! {{int:rv-experiencia:info-virtual}}
+| {{int:rv-experiencia:respuesta-{{{experiencia:info-virtual|}}}}}
+|}
+</div>
+<div style="display:none;">{{#if: {{{experiencia:imagen-destacada|}}} | {{#setmainimage:{{{experiencia:imagen-destacada|}}}}} | {{#setmainimage:{{#replace:https://api.mapbox.com/v4/mapbox.satellite/{{#explode:{{{experiencia:lugar|}}}|, |1}},{{#explode:{{{experiencia:lugar|}}}|, |0}},{{#if: {{{experiencia:imagen-destacada-zoom|}}} | {{{experiencia:imagen-destacada-zoom|}}} | 9 }}/1000x400@2x.png?access_token=pk.eyJ1IjoiaWFjb21lbGxhIiwiYSI6ImNpdWJ3OHJoYTAwOHgyb3BneWd1NG16bjgifQ.8uFt1oMO57yDT9Xzb_ScAw}}| ||}}}}
+[[Experiencia:lugar::{{{experiencia:lugar|}}}]]
+[[Experiencia:info-niveleconomico::{{{experiencia:info-niveleconomico|}}}]]
+[[Experiencia:info-organizacion::{{{experiencia:info-organizacion|}}}]]
+[[Experiencia:info-gestion::{{{experiencia:info-gestion|}}}]]
+[[Experiencia:info-contextosocial::{{{experiencia:info-contextosocial|}}}]]
+[[Experiencia:info-participantes::{{{experiencia:info-participantes|}}}]]
+[[Experiencia:info-tipoeducacion::{{{experiencia:info-tipoeducacion|}}}]]
+[[Experiencia:info-certificacion::{{{experiencia:info-certificacion|}}}]]
+[[Experiencia:info-arancel::{{{experiencia:info-arancel|}}}]]
+[[Experiencia:info-lucro::{{{experiencia:info-lucro|}}}]]
+[[Experiencia:info-voluntariado::{{{experiencia:info-voluntariado|}}}]]
+[[Experiencia:info-virtual::{{{experiencia:info-virtual|}}}]]
+[[Experiencia:contacto-url::{{{experiencia:contacto-url|}}}]]
+[[Experiencia:oldid::{{{experiencia:oldid|}}}]]
+[[Experiencia:pageid::{{PAGEID}}]]
+</div>__NOTOC__
+{{#arraymap:{{{experiencia:info-corrientes|}}}|,|x|[[Categoría:x]] |<nowiki> </nowiki>}}
+{{#arraymap:{{{experiencia:info-enfoques|}}}|,|x|[[Categoría:x]] |<nowiki> </nowiki>}}
+[[Categoría:Experiencia]]
+{{#if: {{{banner-altura|}}} | {{#css: .bannerimage {background-position: 0px {{{banner-altura|}}}% !important;} }} }}
+<div class="clean-p"></div>
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Prensa-Archivo.mw b/bin/wiki/reevoPages/int/Plantilla:Prensa-Archivo.mw
new file mode 100644
index 00000000..fadcad19
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Prensa-Archivo.mw
@@ -0,0 +1,13 @@
+<noinclude>
+<pre>
+{{Prensa-Archivo
+|prensa:archivo:imagen=
+|prensa:archivo:texto=
+}}
+</pre>
+</noinclude>
+<includeonly>
+<blockquote>{{int:rv-prensa:archivo:desc}}</blockquote>
+[[Archivo:{{{prensa:archivo:imagen|}}}|right|300px|Snapshot del sitio web original]]
+{{{prensa:archivo:texto|}}}
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Prensa-Vista.mw b/bin/wiki/reevoPages/int/Plantilla:Prensa-Vista.mw
new file mode 100644
index 00000000..e12cbb2d
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Prensa-Vista.mw
@@ -0,0 +1,21 @@
+<noinclude>
+<pre>
+{{Plantilla:Prensa-Vista}}
+
+{{{1}}} Nombre de la Página (de la Persona)
+{{{2}}} Imagen
+{{{3}}} Fuente
+{{{4}}} PageID
+
+</pre>
+Editar la página para ver el texto de la plantilla.
+</noinclude>
+<includeonly>{{#css:
+ #objeto-{{{4}}} {
+ background-size: cover;
+ height: 300px;
+ background-image: url({{ #ifexist: Archivo:{{{2}}} | {{filepath: {{{2}}}}} | {{filepath:Prensa-sin_imagen.png}} }});
+ position: relative;
+ }
+}}<div class='col-lg-6 col-md-6 col-sm-12 col-xs-12 ficha-personal'><div class='objeto' id='objeto-{{{4}}}'><span class='pie'><span class='objeto-titulo'>{{{1}}}</span><br/>{{#if: {{{3}}}|({{{3}}})}}</span></div></div>
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Prensa.mw b/bin/wiki/reevoPages/int/Plantilla:Prensa.mw
new file mode 100644
index 00000000..afc66cbe
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Prensa.mw
@@ -0,0 +1,49 @@
+<noinclude>
+<pre>
+{{Prensa
+|prensa:url=
+|prensa:fecha=
+|prensa:fuente=
+|prensa:pais=
+|prensa:idioma=
+|prensa:descripcion=
+|prensa:etiquetas=
+|prensa:imagen=
+|prensa:elggid=
+}}
+</pre>
+</noinclude><includeonly>{| class="wikitable"
+|-
+! {{int:rv-prensa:url}}
+| [[Prensa:url::{{{prensa:url|}}}]]
+{{#ifexist: {{FULLPAGENAME}}/Archivo | <small>[[{{FULLPAGENAME}}/Archivo|{{int:rv-prensa:archivo:leyenda}}]]</small> | }}
+|-
+! {{int:rv-prensa:fecha}}
+| [[Prensa:fecha::{{{prensa:fecha|}}}]]
+|-
+! {{int:rv-prensa:fuente}}
+| [[Prensa:fuente::{{{prensa:fuente|}}}]]
+|-
+! {{int:rv-prensa:pais}}
+| {{int:rv-pais-{{{prensa:pais|}}}}}<span style="display:none">[[Prensa:pais::{{{prensa:pais|}}}]]</span>
+|-
+! {{int:rv-prensa:idioma}}
+| [[Prensa:idioma::{{{prensa:idioma|}}}]]
+|-
+! {{int:rv-prensa:descripcion}}
+| [[Prensa:descripcion::{{{prensa:descripcion|}}}]]{{#description2:{{{prensa:descripcion|}}}}}
+|-
+! {{int:rv-prensa:etiquetas}}
+| [[Prensa:etiquetas::{{{prensa:etiquetas|}}}]]
+|}
+
+[[Archivo:{{#setmainimage:{{{prensa:imagen|}}}}}]]
+
+<div style="display:none;">
+[[Prensa:elggid::{{{prensa:elggid|}}}]]
+[[Prensa:imagen::{{{prensa:imagen|}}}]]
+[[Prensa:pageid::{{PAGEID}}]]
+</div>
+
+[[Categoría:Prensa]]
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfil.mw b/bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfil.mw
new file mode 100644
index 00000000..90ff2c55
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfil.mw
@@ -0,0 +1,8 @@
+<includeonly>
+Existe una cuenta con este mismo nombre de usuario en la versión anterior de REEVO, puedes ver dicho perfil siguiente [https://red.reevo.org/profile/{{BASEPAGENAME}} este enlace].
+
+Si es una cuenta tuya, podemos importar los datos de ese usuario a [[Usuario:{{BASEPAGENAME}}|tu perfil en la versión actual de REEVO]]. <b>Ten en cuenta que Se reemplazará todo el contenido de tu perfil actual</b>.
+
+'''{{#ifeq: {{BASEPAGENAME}} | {{#USERNAME:}} | [[Especial:ImportarPerfil|¡Importar datos de mi usuario anterior!]] | Identificate como este usuario para poder realizar la importación desde esta página }}'''
+
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfilMsg.mw b/bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfilMsg.mw
new file mode 100644
index 00000000..c23dd23d
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Sistema-ImportarPerfilMsg.mw
@@ -0,0 +1,3 @@
+<includeonly>
+Existe una cuenta con este mismo nombre de usuario en la versión anterior de REEVO, ¿quieres importar aquí los datos de ese perfil? [[{{FULLPAGENAME}}/ImportarPerfil|¡Sigue este enlace!]]
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Plantilla:Usuario.mw b/bin/wiki/reevoPages/int/Plantilla:Usuario.mw
new file mode 100644
index 00000000..07cc32a2
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Plantilla:Usuario.mw
@@ -0,0 +1,25 @@
+<noinclude>
+<pre>
+{{Usuario
+|usuario:nombre=
+|usuario:descripcion=
+|usuario:web=
+|usuario:avatar=
+}}
+</pre>
+</noinclude><includeonly>{{#ifeq: {{BASEPAGENAME}} | {{#USERNAME:}} | {{#ifexist: {{FULLPAGENAME}}/ImportarPerfil | {{Sistema-ImportarPerfilMsg}} | }} | }}
+{| class="wikitable"
+|-
+! {{int:rv-usuario:nombre}}
+| [[Usuario:nombre::{{{usuario:nombre|}}}]]
+|-
+! {{int:rv-usuario:descripcion}}
+| [[Usuario:descripcion::{{{usuario:descripcion|}}}]]
+|-
+! {{int:rv-usuario:web}}
+| [[Usuario:web::{{{usuario:web|}}}]]
+|}
+[[Imagen:{{{usuario:avatar|}}}]]
+
+[[Categoría:Usuario]]
+</includeonly>
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:autoria.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:autoria.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:autoria.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:duracion.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:duracion.mw
new file mode 100644
index 00000000..151cf481
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:duracion.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Número]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:estreno.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:estreno.mw
new file mode 100644
index 00000000..3c6e9d62
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:estreno.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Fecha]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:genero.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:genero.mw
new file mode 100644
index 00000000..af6c4dae
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:genero.mw
@@ -0,0 +1,5 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::documental]]
+* [[Permite el valor::ficcion]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:magnet.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:magnet.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:magnet.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:pais.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:pais.mw
new file mode 100644
index 00000000..0f2e1b34
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:pais.mw
@@ -0,0 +1,258 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::AC]]
+* [[Permite el valor::AD]]
+* [[Permite el valor::AE]]
+* [[Permite el valor::AF]]
+* [[Permite el valor::AG]]
+* [[Permite el valor::AI]]
+* [[Permite el valor::AL]]
+* [[Permite el valor::AM]]
+* [[Permite el valor::AO]]
+* [[Permite el valor::AQ]]
+* [[Permite el valor::AR]]
+* [[Permite el valor::AS]]
+* [[Permite el valor::AT]]
+* [[Permite el valor::AU]]
+* [[Permite el valor::AW]]
+* [[Permite el valor::AX]]
+* [[Permite el valor::AZ]]
+* [[Permite el valor::BA]]
+* [[Permite el valor::BB]]
+* [[Permite el valor::BD]]
+* [[Permite el valor::BE]]
+* [[Permite el valor::BF]]
+* [[Permite el valor::BG]]
+* [[Permite el valor::BH]]
+* [[Permite el valor::BI]]
+* [[Permite el valor::BJ]]
+* [[Permite el valor::BL]]
+* [[Permite el valor::BM]]
+* [[Permite el valor::BN]]
+* [[Permite el valor::BO]]
+* [[Permite el valor::BQ]]
+* [[Permite el valor::BR]]
+* [[Permite el valor::BS]]
+* [[Permite el valor::BT]]
+* [[Permite el valor::BW]]
+* [[Permite el valor::BY]]
+* [[Permite el valor::BZ]]
+* [[Permite el valor::CA]]
+* [[Permite el valor::CC]]
+* [[Permite el valor::CD]]
+* [[Permite el valor::CF]]
+* [[Permite el valor::CG]]
+* [[Permite el valor::CH]]
+* [[Permite el valor::CI]]
+* [[Permite el valor::CK]]
+* [[Permite el valor::CL]]
+* [[Permite el valor::CM]]
+* [[Permite el valor::CN]]
+* [[Permite el valor::CO]]
+* [[Permite el valor::CR]]
+* [[Permite el valor::CU]]
+* [[Permite el valor::CV]]
+* [[Permite el valor::CW]]
+* [[Permite el valor::CX]]
+* [[Permite el valor::CY]]
+* [[Permite el valor::CZ]]
+* [[Permite el valor::DE]]
+* [[Permite el valor::DG]]
+* [[Permite el valor::DJ]]
+* [[Permite el valor::DK]]
+* [[Permite el valor::DM]]
+* [[Permite el valor::DO]]
+* [[Permite el valor::DZ]]
+* [[Permite el valor::EA]]
+* [[Permite el valor::EC]]
+* [[Permite el valor::EE]]
+* [[Permite el valor::EG]]
+* [[Permite el valor::EH]]
+* [[Permite el valor::ER]]
+* [[Permite el valor::ES]]
+* [[Permite el valor::ET]]
+* [[Permite el valor::EZ]]
+* [[Permite el valor::FI]]
+* [[Permite el valor::FJ]]
+* [[Permite el valor::FK]]
+* [[Permite el valor::FM]]
+* [[Permite el valor::FO]]
+* [[Permite el valor::FR]]
+* [[Permite el valor::GA]]
+* [[Permite el valor::GB]]
+* [[Permite el valor::GD]]
+* [[Permite el valor::GE]]
+* [[Permite el valor::GF]]
+* [[Permite el valor::GG]]
+* [[Permite el valor::GH]]
+* [[Permite el valor::GI]]
+* [[Permite el valor::GL]]
+* [[Permite el valor::GM]]
+* [[Permite el valor::GN]]
+* [[Permite el valor::GP]]
+* [[Permite el valor::GQ]]
+* [[Permite el valor::GR]]
+* [[Permite el valor::GS]]
+* [[Permite el valor::GT]]
+* [[Permite el valor::GU]]
+* [[Permite el valor::GW]]
+* [[Permite el valor::GY]]
+* [[Permite el valor::HK]]
+* [[Permite el valor::HN]]
+* [[Permite el valor::HR]]
+* [[Permite el valor::HT]]
+* [[Permite el valor::HU]]
+* [[Permite el valor::IC]]
+* [[Permite el valor::ID]]
+* [[Permite el valor::IE]]
+* [[Permite el valor::IL]]
+* [[Permite el valor::IM]]
+* [[Permite el valor::IN]]
+* [[Permite el valor::IO]]
+* [[Permite el valor::IQ]]
+* [[Permite el valor::IR]]
+* [[Permite el valor::IS]]
+* [[Permite el valor::IT]]
+* [[Permite el valor::JE]]
+* [[Permite el valor::JM]]
+* [[Permite el valor::JO]]
+* [[Permite el valor::JP]]
+* [[Permite el valor::KE]]
+* [[Permite el valor::KG]]
+* [[Permite el valor::KH]]
+* [[Permite el valor::KI]]
+* [[Permite el valor::KM]]
+* [[Permite el valor::KN]]
+* [[Permite el valor::KP]]
+* [[Permite el valor::KR]]
+* [[Permite el valor::KW]]
+* [[Permite el valor::KY]]
+* [[Permite el valor::KZ]]
+* [[Permite el valor::LA]]
+* [[Permite el valor::LB]]
+* [[Permite el valor::LC]]
+* [[Permite el valor::LI]]
+* [[Permite el valor::LK]]
+* [[Permite el valor::LR]]
+* [[Permite el valor::LS]]
+* [[Permite el valor::LT]]
+* [[Permite el valor::LU]]
+* [[Permite el valor::LV]]
+* [[Permite el valor::LY]]
+* [[Permite el valor::MA]]
+* [[Permite el valor::MC]]
+* [[Permite el valor::MD]]
+* [[Permite el valor::ME]]
+* [[Permite el valor::MF]]
+* [[Permite el valor::MG]]
+* [[Permite el valor::MH]]
+* [[Permite el valor::MK]]
+* [[Permite el valor::ML]]
+* [[Permite el valor::MM]]
+* [[Permite el valor::MN]]
+* [[Permite el valor::MO]]
+* [[Permite el valor::MP]]
+* [[Permite el valor::MQ]]
+* [[Permite el valor::MR]]
+* [[Permite el valor::MS]]
+* [[Permite el valor::MT]]
+* [[Permite el valor::MU]]
+* [[Permite el valor::MV]]
+* [[Permite el valor::MW]]
+* [[Permite el valor::MX]]
+* [[Permite el valor::MY]]
+* [[Permite el valor::MZ]]
+* [[Permite el valor::NA]]
+* [[Permite el valor::NC]]
+* [[Permite el valor::NE]]
+* [[Permite el valor::NF]]
+* [[Permite el valor::NG]]
+* [[Permite el valor::NI]]
+* [[Permite el valor::NL]]
+* [[Permite el valor::NO]]
+* [[Permite el valor::NP]]
+* [[Permite el valor::NR]]
+* [[Permite el valor::NU]]
+* [[Permite el valor::NZ]]
+* [[Permite el valor::OM]]
+* [[Permite el valor::PA]]
+* [[Permite el valor::PE]]
+* [[Permite el valor::PF]]
+* [[Permite el valor::PG]]
+* [[Permite el valor::PH]]
+* [[Permite el valor::PK]]
+* [[Permite el valor::PL]]
+* [[Permite el valor::PM]]
+* [[Permite el valor::PN]]
+* [[Permite el valor::PR]]
+* [[Permite el valor::PS]]
+* [[Permite el valor::PT]]
+* [[Permite el valor::PW]]
+* [[Permite el valor::PY]]
+* [[Permite el valor::QA]]
+* [[Permite el valor::RE]]
+* [[Permite el valor::RO]]
+* [[Permite el valor::RS]]
+* [[Permite el valor::RU]]
+* [[Permite el valor::RW]]
+* [[Permite el valor::SA]]
+* [[Permite el valor::SB]]
+* [[Permite el valor::SC]]
+* [[Permite el valor::SD]]
+* [[Permite el valor::SE]]
+* [[Permite el valor::SG]]
+* [[Permite el valor::SH]]
+* [[Permite el valor::SI]]
+* [[Permite el valor::SJ]]
+* [[Permite el valor::SK]]
+* [[Permite el valor::SL]]
+* [[Permite el valor::SM]]
+* [[Permite el valor::SN]]
+* [[Permite el valor::SO]]
+* [[Permite el valor::SR]]
+* [[Permite el valor::SS]]
+* [[Permite el valor::ST]]
+* [[Permite el valor::SV]]
+* [[Permite el valor::SX]]
+* [[Permite el valor::SY]]
+* [[Permite el valor::SZ]]
+* [[Permite el valor::TA]]
+* [[Permite el valor::TC]]
+* [[Permite el valor::TD]]
+* [[Permite el valor::TF]]
+* [[Permite el valor::TG]]
+* [[Permite el valor::TH]]
+* [[Permite el valor::TJ]]
+* [[Permite el valor::TK]]
+* [[Permite el valor::TL]]
+* [[Permite el valor::TM]]
+* [[Permite el valor::TN]]
+* [[Permite el valor::TO]]
+* [[Permite el valor::TR]]
+* [[Permite el valor::TT]]
+* [[Permite el valor::TV]]
+* [[Permite el valor::TW]]
+* [[Permite el valor::TZ]]
+* [[Permite el valor::UA]]
+* [[Permite el valor::UG]]
+* [[Permite el valor::UM]]
+* [[Permite el valor::UN]]
+* [[Permite el valor::US]]
+* [[Permite el valor::UY]]
+* [[Permite el valor::UZ]]
+* [[Permite el valor::VA]]
+* [[Permite el valor::VC]]
+* [[Permite el valor::VE]]
+* [[Permite el valor::VG]]
+* [[Permite el valor::VI]]
+* [[Permite el valor::VN]]
+* [[Permite el valor::VU]]
+* [[Permite el valor::WF]]
+* [[Permite el valor::WS]]
+* [[Permite el valor::XK]]
+* [[Permite el valor::YE]]
+* [[Permite el valor::YT]]
+* [[Permite el valor::ZA]]
+* [[Permite el valor::ZM]]
+* [[Permite el valor::ZW]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:poster.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:poster.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:poster.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:sub.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:sub.mw
new file mode 100644
index 00000000..67bbc4c5
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:sub.mw
@@ -0,0 +1,6 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::es]]
+* [[Permite el valor::en]]
+* [[Permite el valor::pt]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:titulo.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:titulo.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:titulo.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:url.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:url.mw
new file mode 100644
index 00000000..99d01fb4
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:url.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::URL]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:urltrailer.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:urltrailer.mw
new file mode 100644
index 00000000..99d01fb4
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:urltrailer.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::URL]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Audiovisual:web.mw b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:web.mw
new file mode 100644
index 00000000..99d01fb4
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Audiovisual:web.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::URL]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-email.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-email.mw
new file mode 100644
index 00000000..e64e22f2
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-email.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Dirección electrónica]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-telefono.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-telefono.mw
new file mode 100644
index 00000000..fff74541
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-telefono.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Telephone number]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-url.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-url.mw
new file mode 100644
index 00000000..99d01fb4
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:contacto-url.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::URL]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:descripcion.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:descripcion.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:descripcion.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:imagen-destacada.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:imagen-destacada.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:imagen-destacada.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-certificacion.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-certificacion.mw
new file mode 100644
index 00000000..428006ae
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-certificacion.mw
@@ -0,0 +1,5 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::si]]
+* [[Permite el valor::no]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-contextosocial.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-contextosocial.mw
new file mode 100644
index 00000000..1fe61ae8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-contextosocial.mw
@@ -0,0 +1,8 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::urbano]]
+* [[Permite el valor::rural]]
+* [[Permite el valor::encierro]]
+* [[Permite el valor::domiciliario]]
+* [[Permite el valor::hospitalario]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-corrientes.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-corrientes.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-corrientes.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-final.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-final.mw
new file mode 100644
index 00000000..3c6e9d62
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-final.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Fecha]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-inicio.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-inicio.mw
new file mode 100644
index 00000000..3c6e9d62
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-inicio.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Fecha]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-lucro.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-lucro.mw
new file mode 100644
index 00000000..428006ae
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-lucro.mw
@@ -0,0 +1,5 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::si]]
+* [[Permite el valor::no]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-nivel.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-nivel.mw
new file mode 100644
index 00000000..f9cd11bd
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-nivel.mw
@@ -0,0 +1,9 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::inicial]]
+* [[Permite el valor::primario]]
+* [[Permite el valor::secundario]]
+* [[Permite el valor::terciario]]
+* [[Permite el valor::mayores]]
+* [[Permite el valor::intergeneracional]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-niveleconomico.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-niveleconomico.mw
new file mode 100644
index 00000000..f26269d2
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-niveleconomico.mw
@@ -0,0 +1,7 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::muybajo]]
+* [[Permite el valor::bajo]]
+* [[Permite el valor::medio]]
+* [[Permite el valor::alto]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-participantes.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-participantes.mw
new file mode 100644
index 00000000..151cf481
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-participantes.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Número]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-tipoeducacion.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-tipoeducacion.mw
new file mode 100644
index 00000000..73556ab7
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-tipoeducacion.mw
@@ -0,0 +1,6 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::formal]]
+* [[Permite el valor::noformal]]
+* [[Permite el valor::informal]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-virtual.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-virtual.mw
new file mode 100644
index 00000000..ce300415
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:info-virtual.mw
@@ -0,0 +1,6 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::si]]
+* [[Permite el valor::no]]
+* [[Permite el valor::parcial]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-calle.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-calle.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-calle.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-ciudad.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-ciudad.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-ciudad.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-pais.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-pais.mw
new file mode 100644
index 00000000..97493371
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-pais.mw
@@ -0,0 +1,259 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::Argentina]]
+* [[Permite el valor::AC]]
+* [[Permite el valor::AD]]
+* [[Permite el valor::AE]]
+* [[Permite el valor::AF]]
+* [[Permite el valor::AG]]
+* [[Permite el valor::AI]]
+* [[Permite el valor::AL]]
+* [[Permite el valor::AM]]
+* [[Permite el valor::AO]]
+* [[Permite el valor::AQ]]
+* [[Permite el valor::AR]]
+* [[Permite el valor::AS]]
+* [[Permite el valor::AT]]
+* [[Permite el valor::AU]]
+* [[Permite el valor::AW]]
+* [[Permite el valor::AX]]
+* [[Permite el valor::AZ]]
+* [[Permite el valor::BA]]
+* [[Permite el valor::BB]]
+* [[Permite el valor::BD]]
+* [[Permite el valor::BE]]
+* [[Permite el valor::BF]]
+* [[Permite el valor::BG]]
+* [[Permite el valor::BH]]
+* [[Permite el valor::BI]]
+* [[Permite el valor::BJ]]
+* [[Permite el valor::BL]]
+* [[Permite el valor::BM]]
+* [[Permite el valor::BN]]
+* [[Permite el valor::BO]]
+* [[Permite el valor::BQ]]
+* [[Permite el valor::BR]]
+* [[Permite el valor::BS]]
+* [[Permite el valor::BT]]
+* [[Permite el valor::BW]]
+* [[Permite el valor::BY]]
+* [[Permite el valor::BZ]]
+* [[Permite el valor::CA]]
+* [[Permite el valor::CC]]
+* [[Permite el valor::CD]]
+* [[Permite el valor::CF]]
+* [[Permite el valor::CG]]
+* [[Permite el valor::CH]]
+* [[Permite el valor::CI]]
+* [[Permite el valor::CK]]
+* [[Permite el valor::CL]]
+* [[Permite el valor::CM]]
+* [[Permite el valor::CN]]
+* [[Permite el valor::CO]]
+* [[Permite el valor::CR]]
+* [[Permite el valor::CU]]
+* [[Permite el valor::CV]]
+* [[Permite el valor::CW]]
+* [[Permite el valor::CX]]
+* [[Permite el valor::CY]]
+* [[Permite el valor::CZ]]
+* [[Permite el valor::DE]]
+* [[Permite el valor::DG]]
+* [[Permite el valor::DJ]]
+* [[Permite el valor::DK]]
+* [[Permite el valor::DM]]
+* [[Permite el valor::DO]]
+* [[Permite el valor::DZ]]
+* [[Permite el valor::EA]]
+* [[Permite el valor::EC]]
+* [[Permite el valor::EE]]
+* [[Permite el valor::EG]]
+* [[Permite el valor::EH]]
+* [[Permite el valor::ER]]
+* [[Permite el valor::ES]]
+* [[Permite el valor::ET]]
+* [[Permite el valor::EZ]]
+* [[Permite el valor::FI]]
+* [[Permite el valor::FJ]]
+* [[Permite el valor::FK]]
+* [[Permite el valor::FM]]
+* [[Permite el valor::FO]]
+* [[Permite el valor::FR]]
+* [[Permite el valor::GA]]
+* [[Permite el valor::GB]]
+* [[Permite el valor::GD]]
+* [[Permite el valor::GE]]
+* [[Permite el valor::GF]]
+* [[Permite el valor::GG]]
+* [[Permite el valor::GH]]
+* [[Permite el valor::GI]]
+* [[Permite el valor::GL]]
+* [[Permite el valor::GM]]
+* [[Permite el valor::GN]]
+* [[Permite el valor::GP]]
+* [[Permite el valor::GQ]]
+* [[Permite el valor::GR]]
+* [[Permite el valor::GS]]
+* [[Permite el valor::GT]]
+* [[Permite el valor::GU]]
+* [[Permite el valor::GW]]
+* [[Permite el valor::GY]]
+* [[Permite el valor::HK]]
+* [[Permite el valor::HN]]
+* [[Permite el valor::HR]]
+* [[Permite el valor::HT]]
+* [[Permite el valor::HU]]
+* [[Permite el valor::IC]]
+* [[Permite el valor::ID]]
+* [[Permite el valor::IE]]
+* [[Permite el valor::IL]]
+* [[Permite el valor::IM]]
+* [[Permite el valor::IN]]
+* [[Permite el valor::IO]]
+* [[Permite el valor::IQ]]
+* [[Permite el valor::IR]]
+* [[Permite el valor::IS]]
+* [[Permite el valor::IT]]
+* [[Permite el valor::JE]]
+* [[Permite el valor::JM]]
+* [[Permite el valor::JO]]
+* [[Permite el valor::JP]]
+* [[Permite el valor::KE]]
+* [[Permite el valor::KG]]
+* [[Permite el valor::KH]]
+* [[Permite el valor::KI]]
+* [[Permite el valor::KM]]
+* [[Permite el valor::KN]]
+* [[Permite el valor::KP]]
+* [[Permite el valor::KR]]
+* [[Permite el valor::KW]]
+* [[Permite el valor::KY]]
+* [[Permite el valor::KZ]]
+* [[Permite el valor::LA]]
+* [[Permite el valor::LB]]
+* [[Permite el valor::LC]]
+* [[Permite el valor::LI]]
+* [[Permite el valor::LK]]
+* [[Permite el valor::LR]]
+* [[Permite el valor::LS]]
+* [[Permite el valor::LT]]
+* [[Permite el valor::LU]]
+* [[Permite el valor::LV]]
+* [[Permite el valor::LY]]
+* [[Permite el valor::MA]]
+* [[Permite el valor::MC]]
+* [[Permite el valor::MD]]
+* [[Permite el valor::ME]]
+* [[Permite el valor::MF]]
+* [[Permite el valor::MG]]
+* [[Permite el valor::MH]]
+* [[Permite el valor::MK]]
+* [[Permite el valor::ML]]
+* [[Permite el valor::MM]]
+* [[Permite el valor::MN]]
+* [[Permite el valor::MO]]
+* [[Permite el valor::MP]]
+* [[Permite el valor::MQ]]
+* [[Permite el valor::MR]]
+* [[Permite el valor::MS]]
+* [[Permite el valor::MT]]
+* [[Permite el valor::MU]]
+* [[Permite el valor::MV]]
+* [[Permite el valor::MW]]
+* [[Permite el valor::MX]]
+* [[Permite el valor::MY]]
+* [[Permite el valor::MZ]]
+* [[Permite el valor::NA]]
+* [[Permite el valor::NC]]
+* [[Permite el valor::NE]]
+* [[Permite el valor::NF]]
+* [[Permite el valor::NG]]
+* [[Permite el valor::NI]]
+* [[Permite el valor::NL]]
+* [[Permite el valor::NO]]
+* [[Permite el valor::NP]]
+* [[Permite el valor::NR]]
+* [[Permite el valor::NU]]
+* [[Permite el valor::NZ]]
+* [[Permite el valor::OM]]
+* [[Permite el valor::PA]]
+* [[Permite el valor::PE]]
+* [[Permite el valor::PF]]
+* [[Permite el valor::PG]]
+* [[Permite el valor::PH]]
+* [[Permite el valor::PK]]
+* [[Permite el valor::PL]]
+* [[Permite el valor::PM]]
+* [[Permite el valor::PN]]
+* [[Permite el valor::PR]]
+* [[Permite el valor::PS]]
+* [[Permite el valor::PT]]
+* [[Permite el valor::PW]]
+* [[Permite el valor::PY]]
+* [[Permite el valor::QA]]
+* [[Permite el valor::RE]]
+* [[Permite el valor::RO]]
+* [[Permite el valor::RS]]
+* [[Permite el valor::RU]]
+* [[Permite el valor::RW]]
+* [[Permite el valor::SA]]
+* [[Permite el valor::SB]]
+* [[Permite el valor::SC]]
+* [[Permite el valor::SD]]
+* [[Permite el valor::SE]]
+* [[Permite el valor::SG]]
+* [[Permite el valor::SH]]
+* [[Permite el valor::SI]]
+* [[Permite el valor::SJ]]
+* [[Permite el valor::SK]]
+* [[Permite el valor::SL]]
+* [[Permite el valor::SM]]
+* [[Permite el valor::SN]]
+* [[Permite el valor::SO]]
+* [[Permite el valor::SR]]
+* [[Permite el valor::SS]]
+* [[Permite el valor::ST]]
+* [[Permite el valor::SV]]
+* [[Permite el valor::SX]]
+* [[Permite el valor::SY]]
+* [[Permite el valor::SZ]]
+* [[Permite el valor::TA]]
+* [[Permite el valor::TC]]
+* [[Permite el valor::TD]]
+* [[Permite el valor::TF]]
+* [[Permite el valor::TG]]
+* [[Permite el valor::TH]]
+* [[Permite el valor::TJ]]
+* [[Permite el valor::TK]]
+* [[Permite el valor::TL]]
+* [[Permite el valor::TM]]
+* [[Permite el valor::TN]]
+* [[Permite el valor::TO]]
+* [[Permite el valor::TR]]
+* [[Permite el valor::TT]]
+* [[Permite el valor::TV]]
+* [[Permite el valor::TW]]
+* [[Permite el valor::TZ]]
+* [[Permite el valor::UA]]
+* [[Permite el valor::UG]]
+* [[Permite el valor::UM]]
+* [[Permite el valor::UN]]
+* [[Permite el valor::US]]
+* [[Permite el valor::UY]]
+* [[Permite el valor::UZ]]
+* [[Permite el valor::VA]]
+* [[Permite el valor::VC]]
+* [[Permite el valor::VE]]
+* [[Permite el valor::VG]]
+* [[Permite el valor::VI]]
+* [[Permite el valor::VN]]
+* [[Permite el valor::VU]]
+* [[Permite el valor::WF]]
+* [[Permite el valor::WS]]
+* [[Permite el valor::XK]]
+* [[Permite el valor::YE]]
+* [[Permite el valor::YT]]
+* [[Permite el valor::ZA]]
+* [[Permite el valor::ZM]]
+* [[Permite el valor::ZW]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-provincia.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-provincia.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar-provincia.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar.mw b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar.mw
new file mode 100644
index 00000000..b97dda88
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Experiencia:lugar.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Coordenadas geográficas]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:descripcion.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:descripcion.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:descripcion.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:elggid.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:elggid.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:elggid.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:etiquetas.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:etiquetas.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:etiquetas.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:fecha.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:fecha.mw
new file mode 100644
index 00000000..3c6e9d62
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:fecha.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Fecha]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:fuente.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:fuente.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:fuente.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:idioma.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:idioma.mw
new file mode 100644
index 00000000..67bbc4c5
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:idioma.mw
@@ -0,0 +1,6 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::es]]
+* [[Permite el valor::en]]
+* [[Permite el valor::pt]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:imagen.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:imagen.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:imagen.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:pageid.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:pageid.mw
new file mode 100644
index 00000000..73f546f8
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:pageid.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:pais.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:pais.mw
new file mode 100644
index 00000000..0f2e1b34
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:pais.mw
@@ -0,0 +1,258 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::Texto]].
+
+Los valores permitidos para esta propiedad son:
+* [[Permite el valor::AC]]
+* [[Permite el valor::AD]]
+* [[Permite el valor::AE]]
+* [[Permite el valor::AF]]
+* [[Permite el valor::AG]]
+* [[Permite el valor::AI]]
+* [[Permite el valor::AL]]
+* [[Permite el valor::AM]]
+* [[Permite el valor::AO]]
+* [[Permite el valor::AQ]]
+* [[Permite el valor::AR]]
+* [[Permite el valor::AS]]
+* [[Permite el valor::AT]]
+* [[Permite el valor::AU]]
+* [[Permite el valor::AW]]
+* [[Permite el valor::AX]]
+* [[Permite el valor::AZ]]
+* [[Permite el valor::BA]]
+* [[Permite el valor::BB]]
+* [[Permite el valor::BD]]
+* [[Permite el valor::BE]]
+* [[Permite el valor::BF]]
+* [[Permite el valor::BG]]
+* [[Permite el valor::BH]]
+* [[Permite el valor::BI]]
+* [[Permite el valor::BJ]]
+* [[Permite el valor::BL]]
+* [[Permite el valor::BM]]
+* [[Permite el valor::BN]]
+* [[Permite el valor::BO]]
+* [[Permite el valor::BQ]]
+* [[Permite el valor::BR]]
+* [[Permite el valor::BS]]
+* [[Permite el valor::BT]]
+* [[Permite el valor::BW]]
+* [[Permite el valor::BY]]
+* [[Permite el valor::BZ]]
+* [[Permite el valor::CA]]
+* [[Permite el valor::CC]]
+* [[Permite el valor::CD]]
+* [[Permite el valor::CF]]
+* [[Permite el valor::CG]]
+* [[Permite el valor::CH]]
+* [[Permite el valor::CI]]
+* [[Permite el valor::CK]]
+* [[Permite el valor::CL]]
+* [[Permite el valor::CM]]
+* [[Permite el valor::CN]]
+* [[Permite el valor::CO]]
+* [[Permite el valor::CR]]
+* [[Permite el valor::CU]]
+* [[Permite el valor::CV]]
+* [[Permite el valor::CW]]
+* [[Permite el valor::CX]]
+* [[Permite el valor::CY]]
+* [[Permite el valor::CZ]]
+* [[Permite el valor::DE]]
+* [[Permite el valor::DG]]
+* [[Permite el valor::DJ]]
+* [[Permite el valor::DK]]
+* [[Permite el valor::DM]]
+* [[Permite el valor::DO]]
+* [[Permite el valor::DZ]]
+* [[Permite el valor::EA]]
+* [[Permite el valor::EC]]
+* [[Permite el valor::EE]]
+* [[Permite el valor::EG]]
+* [[Permite el valor::EH]]
+* [[Permite el valor::ER]]
+* [[Permite el valor::ES]]
+* [[Permite el valor::ET]]
+* [[Permite el valor::EZ]]
+* [[Permite el valor::FI]]
+* [[Permite el valor::FJ]]
+* [[Permite el valor::FK]]
+* [[Permite el valor::FM]]
+* [[Permite el valor::FO]]
+* [[Permite el valor::FR]]
+* [[Permite el valor::GA]]
+* [[Permite el valor::GB]]
+* [[Permite el valor::GD]]
+* [[Permite el valor::GE]]
+* [[Permite el valor::GF]]
+* [[Permite el valor::GG]]
+* [[Permite el valor::GH]]
+* [[Permite el valor::GI]]
+* [[Permite el valor::GL]]
+* [[Permite el valor::GM]]
+* [[Permite el valor::GN]]
+* [[Permite el valor::GP]]
+* [[Permite el valor::GQ]]
+* [[Permite el valor::GR]]
+* [[Permite el valor::GS]]
+* [[Permite el valor::GT]]
+* [[Permite el valor::GU]]
+* [[Permite el valor::GW]]
+* [[Permite el valor::GY]]
+* [[Permite el valor::HK]]
+* [[Permite el valor::HN]]
+* [[Permite el valor::HR]]
+* [[Permite el valor::HT]]
+* [[Permite el valor::HU]]
+* [[Permite el valor::IC]]
+* [[Permite el valor::ID]]
+* [[Permite el valor::IE]]
+* [[Permite el valor::IL]]
+* [[Permite el valor::IM]]
+* [[Permite el valor::IN]]
+* [[Permite el valor::IO]]
+* [[Permite el valor::IQ]]
+* [[Permite el valor::IR]]
+* [[Permite el valor::IS]]
+* [[Permite el valor::IT]]
+* [[Permite el valor::JE]]
+* [[Permite el valor::JM]]
+* [[Permite el valor::JO]]
+* [[Permite el valor::JP]]
+* [[Permite el valor::KE]]
+* [[Permite el valor::KG]]
+* [[Permite el valor::KH]]
+* [[Permite el valor::KI]]
+* [[Permite el valor::KM]]
+* [[Permite el valor::KN]]
+* [[Permite el valor::KP]]
+* [[Permite el valor::KR]]
+* [[Permite el valor::KW]]
+* [[Permite el valor::KY]]
+* [[Permite el valor::KZ]]
+* [[Permite el valor::LA]]
+* [[Permite el valor::LB]]
+* [[Permite el valor::LC]]
+* [[Permite el valor::LI]]
+* [[Permite el valor::LK]]
+* [[Permite el valor::LR]]
+* [[Permite el valor::LS]]
+* [[Permite el valor::LT]]
+* [[Permite el valor::LU]]
+* [[Permite el valor::LV]]
+* [[Permite el valor::LY]]
+* [[Permite el valor::MA]]
+* [[Permite el valor::MC]]
+* [[Permite el valor::MD]]
+* [[Permite el valor::ME]]
+* [[Permite el valor::MF]]
+* [[Permite el valor::MG]]
+* [[Permite el valor::MH]]
+* [[Permite el valor::MK]]
+* [[Permite el valor::ML]]
+* [[Permite el valor::MM]]
+* [[Permite el valor::MN]]
+* [[Permite el valor::MO]]
+* [[Permite el valor::MP]]
+* [[Permite el valor::MQ]]
+* [[Permite el valor::MR]]
+* [[Permite el valor::MS]]
+* [[Permite el valor::MT]]
+* [[Permite el valor::MU]]
+* [[Permite el valor::MV]]
+* [[Permite el valor::MW]]
+* [[Permite el valor::MX]]
+* [[Permite el valor::MY]]
+* [[Permite el valor::MZ]]
+* [[Permite el valor::NA]]
+* [[Permite el valor::NC]]
+* [[Permite el valor::NE]]
+* [[Permite el valor::NF]]
+* [[Permite el valor::NG]]
+* [[Permite el valor::NI]]
+* [[Permite el valor::NL]]
+* [[Permite el valor::NO]]
+* [[Permite el valor::NP]]
+* [[Permite el valor::NR]]
+* [[Permite el valor::NU]]
+* [[Permite el valor::NZ]]
+* [[Permite el valor::OM]]
+* [[Permite el valor::PA]]
+* [[Permite el valor::PE]]
+* [[Permite el valor::PF]]
+* [[Permite el valor::PG]]
+* [[Permite el valor::PH]]
+* [[Permite el valor::PK]]
+* [[Permite el valor::PL]]
+* [[Permite el valor::PM]]
+* [[Permite el valor::PN]]
+* [[Permite el valor::PR]]
+* [[Permite el valor::PS]]
+* [[Permite el valor::PT]]
+* [[Permite el valor::PW]]
+* [[Permite el valor::PY]]
+* [[Permite el valor::QA]]
+* [[Permite el valor::RE]]
+* [[Permite el valor::RO]]
+* [[Permite el valor::RS]]
+* [[Permite el valor::RU]]
+* [[Permite el valor::RW]]
+* [[Permite el valor::SA]]
+* [[Permite el valor::SB]]
+* [[Permite el valor::SC]]
+* [[Permite el valor::SD]]
+* [[Permite el valor::SE]]
+* [[Permite el valor::SG]]
+* [[Permite el valor::SH]]
+* [[Permite el valor::SI]]
+* [[Permite el valor::SJ]]
+* [[Permite el valor::SK]]
+* [[Permite el valor::SL]]
+* [[Permite el valor::SM]]
+* [[Permite el valor::SN]]
+* [[Permite el valor::SO]]
+* [[Permite el valor::SR]]
+* [[Permite el valor::SS]]
+* [[Permite el valor::ST]]
+* [[Permite el valor::SV]]
+* [[Permite el valor::SX]]
+* [[Permite el valor::SY]]
+* [[Permite el valor::SZ]]
+* [[Permite el valor::TA]]
+* [[Permite el valor::TC]]
+* [[Permite el valor::TD]]
+* [[Permite el valor::TF]]
+* [[Permite el valor::TG]]
+* [[Permite el valor::TH]]
+* [[Permite el valor::TJ]]
+* [[Permite el valor::TK]]
+* [[Permite el valor::TL]]
+* [[Permite el valor::TM]]
+* [[Permite el valor::TN]]
+* [[Permite el valor::TO]]
+* [[Permite el valor::TR]]
+* [[Permite el valor::TT]]
+* [[Permite el valor::TV]]
+* [[Permite el valor::TW]]
+* [[Permite el valor::TZ]]
+* [[Permite el valor::UA]]
+* [[Permite el valor::UG]]
+* [[Permite el valor::UM]]
+* [[Permite el valor::UN]]
+* [[Permite el valor::US]]
+* [[Permite el valor::UY]]
+* [[Permite el valor::UZ]]
+* [[Permite el valor::VA]]
+* [[Permite el valor::VC]]
+* [[Permite el valor::VE]]
+* [[Permite el valor::VG]]
+* [[Permite el valor::VI]]
+* [[Permite el valor::VN]]
+* [[Permite el valor::VU]]
+* [[Permite el valor::WF]]
+* [[Permite el valor::WS]]
+* [[Permite el valor::XK]]
+* [[Permite el valor::YE]]
+* [[Permite el valor::YT]]
+* [[Permite el valor::ZA]]
+* [[Permite el valor::ZM]]
+* [[Permite el valor::ZW]]
diff --git a/bin/wiki/reevoPages/int/Propiedad:Prensa:url.mw b/bin/wiki/reevoPages/int/Propiedad:Prensa:url.mw
new file mode 100644
index 00000000..99d01fb4
--- /dev/null
+++ b/bin/wiki/reevoPages/int/Propiedad:Prensa:url.mw
@@ -0,0 +1 @@
+Esta es una propiedad de tipo [[Tiene tipo de datos::URL]].
diff --git a/bin/wiki/reevoPages/int/REEVO:Audiovisual.mw b/bin/wiki/reevoPages/int/REEVO:Audiovisual.mw
new file mode 100644
index 00000000..016cef9d
--- /dev/null
+++ b/bin/wiki/reevoPages/int/REEVO:Audiovisual.mw
@@ -0,0 +1 @@
+{{#default_form:Audiovisual}}
diff --git a/bin/wiki/reevoPages/int/REEVO:Experiencia.mw b/bin/wiki/reevoPages/int/REEVO:Experiencia.mw
new file mode 100644
index 00000000..0b2593ee
--- /dev/null
+++ b/bin/wiki/reevoPages/int/REEVO:Experiencia.mw
@@ -0,0 +1 @@
+{{#default_form:Experiencia}}
diff --git a/bin/wiki/reevoPages/int/REEVO:Prensa.mw b/bin/wiki/reevoPages/int/REEVO:Prensa.mw
new file mode 100644
index 00000000..9b094401
--- /dev/null
+++ b/bin/wiki/reevoPages/int/REEVO:Prensa.mw
@@ -0,0 +1 @@
+{{#default_form:Prensa}}
diff --git a/bin/wiki/reevoPages/int/REEVO:Usuario.mw b/bin/wiki/reevoPages/int/REEVO:Usuario.mw
new file mode 100644
index 00000000..2e73fb55
--- /dev/null
+++ b/bin/wiki/reevoPages/int/REEVO:Usuario.mw
@@ -0,0 +1 @@
+{{#default_form:Usuario}}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/.gitignore b/bin/wiki/vendor/addwiki/mediawiki-api-base/.gitignore
new file mode 100644
index 00000000..9c16bff2
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/.gitignore
@@ -0,0 +1,6 @@
+.idea
+vendor
+composer.lock
+test.php
+docs/_build
+phpunit.xml
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/.phan/config.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/.phan/config.php
new file mode 100644
index 00000000..28c1f4ad
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/.phan/config.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * This configuration will be read and overlaid on top of the
+ * default configuration. Command line arguments will be applied
+ * after this file is read.
+ */
+return [
+
+ /**
+ * A list of directories that should be parsed for class and
+ * method information. After excluding the directories
+ * defined in exclude_analysis_directory_list, the remaining
+ * files will be statically analyzed for errors.
+ *
+ * Thus, both first-party and third-party code being used by
+ * your application should be included in this list.
+ */
+ 'directory_list' => [
+ 'src',
+ 'vendor',
+ ],
+
+ /**
+ * A directory list that defines files that will be excluded
+ * from static analysis, but whose class and method
+ * information should be included.
+ *
+ * Generally, you'll want to include the directories for
+ * third-party code (such as "vendor/") in this list.
+ *
+ * n.b.: If you'd like to parse but not analyze 3rd
+ * party code, directories containing that code
+ * should be added to the `directory_list` as
+ * to `exclude_analysis_directory_list`.
+ */
+ "exclude_analysis_directory_list" => [
+ 'vendor/'
+ ],
+];
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/.scrutinizer.yml b/bin/wiki/vendor/addwiki/mediawiki-api-base/.scrutinizer.yml
new file mode 100644
index 00000000..ffc976e3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/.scrutinizer.yml
@@ -0,0 +1,13 @@
+inherit: true
+
+tools:
+ php_code_sniffer: true
+ php_cpd: true
+ php_cs_fixer: true
+ php_loc: true
+ php_mess_detector: true
+ php_pdepend: true
+ php_analyzer: true
+ sensiolabs_security_checker: true
+ external_code_coverage:
+ timeout: 300 \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/.travis.yml b/bin/wiki/vendor/addwiki/mediawiki-api-base/.travis.yml
new file mode 100644
index 00000000..cb3e7641
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/.travis.yml
@@ -0,0 +1,61 @@
+language: php
+
+php:
+ - hhvm
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+
+env:
+ - MW=master
+
+matrix:
+ include:
+ - php: 7.1
+ env: MW=REL1_28
+ - php: 7.1
+ env: MW=REL1_27
+ - php: 7.1
+ env: MW=REL1_26
+ - php: 7.1
+ env: MW=REL1_25
+ - php: 7.1
+ env: MW=REL1_24
+ allow_failures:
+ - env: MW=REL1_24
+
+addons:
+ mariadb: '10.0'
+
+before_install:
+ - bash ./build/travis/install-mediawiki.sh
+
+install:
+ - travis_retry composer install
+
+before_script:
+ - bash ./build/travis/run-webserver.sh
+ - export ADDWIKI_MW_API='http://localhost:8080/w/api.php'
+ - export ADDWIKI_MW_USER='CIUser'
+ - export ADDWIKI_MW_PASSWORD='CIPass'
+
+script:
+ - composer lint
+ - composer phpcs
+ - composer phpunit-coverage
+
+after_success:
+ - travis_retry wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
+
+cache:
+ directories:
+ - $HOME/.composer/cache
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net##add"
+ on_success: change
+ on_failure: always
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/LICENSE.md b/bin/wiki/vendor/addwiki/mediawiki-api-base/LICENSE.md
new file mode 100644
index 00000000..0671f06a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/LICENSE.md
@@ -0,0 +1,264 @@
+The GNU General Public License, Version 2, June 1991 (GPLv2)
+============================================================
+
+> Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+
+Preamble
+--------
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you can
+do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's free
+use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+
+Terms And Conditions For Copying, Distribution And Modification
+---------------------------------------------------------------
+
+**0.** This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program or
+work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included without
+limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+**1.** You may copy and distribute verbatim copies of the Program's source code
+as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the Program
+a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+**2.** You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+* **a)** You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+* **b)** You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof, to
+ be licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+* **c)** If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the entire whole,
+and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+**3.** You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+* **a)** Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above on
+ a medium customarily used for software interchange; or,
+
+* **b)** Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+* **c)** Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only for
+ noncommercial distribution and only if you received the program in object
+ code or executable form with such an offer, in accord with Subsection b
+ above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code distributed
+need not include anything that is normally distributed (in either source or
+binary form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component itself
+accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source code
+from the same place counts as distribution of the source code, even though third
+parties are not compelled to copy the source along with the object code.
+
+**4.** You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+**5.** You are not required to accept this License, since you have not signed
+it. However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you do
+not accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+**6.** Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+**7.** If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution of
+the Program by all those who receive copies directly or indirectly through you,
+then the only way you could satisfy both it and this License would be to refrain
+entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and the
+section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+**8.** If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+**9.** The Free Software Foundation may publish revised and/or new versions of
+the General Public License from time to time. Such new versions will be similar
+in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that version
+or of any later version published by the Free Software Foundation. If the
+Program does not specify a version number of this License, you may choose any
+version ever published by the Free Software Foundation.
+
+**10.** If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+
+No Warranty
+-----------
+
+**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/README.md b/bin/wiki/vendor/addwiki/mediawiki-api-base/README.md
new file mode 100644
index 00000000..ea2a7d4b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/README.md
@@ -0,0 +1,16 @@
+# mediawiki-api-base
+
+[![Build Status](https://travis-ci.org/addwiki/mediawiki-api-base.svg?branch=master)](https://travis-ci.org/addwiki/mediawiki-api-base)
+[![Code Coverage](https://scrutinizer-ci.com/g/addwiki/mediawiki-api-base/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/addwiki/mediawiki-api-base/?branch=master)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/addwiki/mediawiki-api-base/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/addwiki/mediawiki-api-base/?branch=master)
+[![Dependency Status](https://www.versioneye.com/php/addwiki:mediawiki-api-base/badge?style=flat-square)](https://www.versioneye.com/php/addwiki:mediawiki-api-base)
+
+[![Tested Against](http://php-eye.com/badge/addwiki/mediawiki-api-base/tested.svg)](https://php-eye.com/package/addwiki/mediawiki-api-base)
+
+[![Latest Stable Version](https://poser.pugx.org/addwiki/mediawiki-api-base/version.png)](https://packagist.org/packages/addwiki/mediawiki-api-base)
+[![Download count](https://poser.pugx.org/addwiki/mediawiki-api-base/d/total.png)](https://packagist.org/packages/addwiki/mediawiki-api-base)
+[![Reference Status](https://www.versioneye.com/php/addwiki:mediawiki-api-base/reference_badge.svg?style=flat-square)](https://www.versioneye.com/php/addwiki:mediawiki-api-base/references)
+
+Issue tracker: https://phabricator.wikimedia.org/project/profile/1490/
+
+Documentation: https://addwiki.readthedocs.io \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/RELEASENOTES.md b/bin/wiki/vendor/addwiki/mediawiki-api-base/RELEASENOTES.md
new file mode 100644
index 00000000..a84b19cc
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/RELEASENOTES.md
@@ -0,0 +1,95 @@
+These are the release notes for the [mediawiki-api-base](README.md) library.
+
+## Version 2.4.0 (2 November 2017)
+* New MultipartRequest class added. PR [#38](https://github.com/addwiki/mediawiki-api-base/pull/38).
+
+## Version 2.3.1 (3 May 2017)
+* Don't fail on libxml errors if the RSD URL can still be found. PR [#35](https://github.com/addwiki/mediawiki-api-base/pull/35), Fixes [T163527](https://phabricator.wikimedia.org/T163527).
+
+## Version 2.3.0 (27 April 2017)
+
+* All guzzle configuration settings can now be overridden in `ClientFactory`. [#27](https://github.com/addwiki/mediawiki-api-base/pull/27)
+* Requests that fail due to maxlag will be automatically retried. [#28](https://github.com/addwiki/mediawiki-api-base/pull/28). Fixes [T143193](https://phabricator.wikimedia.org/T143193).
+* Added `MediawikiApi::getApiUrl`. [#24](https://github.com/addwiki/mediawiki-api-base/pull/24)
+* Debugging infomation now logged when login fails. [#26](https://github.com/addwiki/mediawiki-api-base/pull/26)
+* UsageException messages now include the error code and result the API returned. [#31](https://github.com/addwiki/mediawiki-api-base/pull/31)
+* Both formatversion=2 and old style API results supported [#33](https://github.com/addwiki/mediawiki-api-base/pull/33)
+* Fix [MediawikiApi::newFromPage() fails on non-XML HTML](https://phabricator.wikimedia.org/T163527). [#34](https://github.com/addwiki/mediawiki-api-base/pull/34)
+* Various CI improvements.
+
+## Version 2.2.1 (3 August 2016)
+
+* Cast SimpleXMLElements attributes as string in `MediawikiApi::newFromPage()`
+
+## Version 2.2.0 (18 January 2016)
+
+* Added `MediawikiApiInterface`, now implemented by `MediawikiApi`
+* Added `ApiRequester`, now implemented by `MediawikiApi`
+* Added `AsyncApiRequester`, now implemented by `MediawikiApi`
+* The constructor of `MediawikiApi` was made package public
+
+## Version 2.1.0 (29 December 2015)
+
+* Retry throttled actions that return a failed-save code and anti-abuse message
+* Added delay between retried requests
+* Added and used `Guzzle/ClientFactory`
+
+## Version 2.0.0 (18 December 2015)
+
+* Added `MediawikiApi::newFromApiEndpoint` and `MediawikiApi::newFromPage`
+* MediawikiApi constructor access marked as private (please use static factory methods)
+* Added async methods to MediawikiApi `getRequestAsync` & `postRequestAsync`
+* Requires "guzzlehttp/guzzle": "~6.0" ( From "guzzle/guzzle": "~5.2" )
+* Requires "guzzlehttp/promises": "~1.0"
+
+## Version 1.1.1 (20 July 2016)
+
+* Issue with README fixed
+
+## Version 1.1.0 (5 September 2015)
+
+* Requests that encounter a connection exception are now retried
+* Requests that result in non blocking mediawiki api error codes are now retried (ratelimited, readonly, internal_api_error_DBQueryError)
+* MediawikiApi now implements PSR-3 LoggerAwareInterface
+* MediawikiSession now implements PSR-3 LoggerAwareInterface
+* MediawikiApi no longer raises PHP warnings, instead it logs warnings
+
+## Version 1.0.0 (23 August 2015)
+
+* Added `FluentRequest` object
+* Requires "guzzlehttp/retry-subscriber": "~2.0"
+
+## Version 0.3 (1 June 2015)
+
+* UsageExceptions can now contain the full api result array
+* No longer uses addwiki/guzzle-mediawiki-client
+* Now using "guzzlehttp/guzzle": "~5.0" ( From "guzzle/guzzle": "~3.2" )
+* Added getHeaders method to Request interface
+* ApiUser now accepts a domain
+
+## Version 0.2 (13 January 2015)
+
+### Compatibility changes
+
+* Session objects now use action=query&meta=tokens to get tokens when possible.
+NOTE: [Token names have changed between versions](//www.mediawiki.org/wiki/API:Tokens)
+
+### Deprecations
+
+* MediawikiApi getAction and postAction methods have been deprecated in favour of getRequest and postRequest
+
+### New features
+
+* If warnings are present in API results E_USER_WARNING errors are triggered
+* The Request interface and SimpleRequest class have been added
+* MediawikiApi now has a getRequest and postRequest method
+* MediawikiApi now has a getVersion method
+* Unsuccessful logins now throw a UsageException with extra details
+
+## Version 0.1.2 (25 May 2014)
+
+* Fix issue where API tokens were not returned
+
+## Version 0.1 (12 May 2014)
+
+* Initial release after split from mediawiki-api lib
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/install-mediawiki.sh b/bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/install-mediawiki.sh
new file mode 100644
index 00000000..59d29ba4
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/install-mediawiki.sh
@@ -0,0 +1,29 @@
+#! /bin/bash
+
+set -x
+
+originalDirectory=$(pwd)
+
+if [[ $TRAVIS_PHP_VERSION == *"hhvm"* ]]
+then
+ PHPINI=/etc/hhvm/php.ini
+ echo "hhvm.enable_zend_compat = true" >> $PHPINI
+fi
+
+mkdir ./../web
+cd ./../web
+
+wget https://github.com/wikimedia/mediawiki/archive/$MW.tar.gz
+tar -zxf $MW.tar.gz
+mv mediawiki-$MW w
+ln -s ./w ./wiki
+
+cd w
+
+composer self-update
+composer install
+
+mysql -e 'CREATE DATABASE mediawiki;'
+php maintenance/install.php --dbtype mysql --dbuser root --dbname mediawiki --dbpath $(pwd) --pass CIPass TravisWiki CIUser
+
+cd $originalDirectory
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/run-webserver.sh b/bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/run-webserver.sh
new file mode 100644
index 00000000..2412031c
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/build/travis/run-webserver.sh
@@ -0,0 +1,25 @@
+#! /bin/bash
+
+set -x
+
+# HHVM doesn't have a built in web server
+# We can't guarantee any single PHP version is always installed on Travis hosts
+# So list the versions and try to pick the latest PHP version
+# Also the web server on 7.1 seems to have issues, so don't use that?
+if [[ $TRAVIS_PHP_VERSION == *"hhvm"* ]] || [[ $TRAVIS_PHP_VERSION == *"7.1"* ]]
+then
+ WEBSERVERPHPVERSION=`phpenv versions | grep -v system | grep -v hhvm | grep -v 7.1 | tail -n 1 | xargs`
+ phpenv global $WEBSERVERPHPVERSION
+ php --version
+fi
+
+# Run a web server for MediaWiki and wait until it is up
+nohup php -S 0.0.0.0:8080 -t ./../web > /dev/null 2>&1 &
+until curl -s localhost:8080; do true; done > /dev/null 2>&1
+
+# Switch back to the actual php version requested for this build if needed
+if [ -v $WEBSERVERPHPVERSION ]
+then
+ phpenv global $TRAVIS_PHP_VERSION
+ php --version
+fi
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/composer.json b/bin/wiki/vendor/addwiki/mediawiki-api-base/composer.json
new file mode 100644
index 00000000..c83fca09
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/composer.json
@@ -0,0 +1,52 @@
+{
+ "name": "addwiki/mediawiki-api-base",
+ "type": "library",
+ "description": "A basic Mediawiki api base library",
+ "keywords": ["Mediawiki"],
+ "license": "GPL-2.0+",
+ "authors": [
+ { "name": "Addshore" }
+ ],
+ "require": {
+ "php": ">=5.5",
+ "guzzlehttp/guzzle": "~6.0",
+ "guzzlehttp/promises": "~1.0",
+ "psr/log": "~1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.0|~5.3.0",
+ "jakub-onderka/php-parallel-lint": "0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0"
+ },
+ "suggest": {
+ "etsy/phan": "Allows running static analysis on the package (requires PHP 7+)"
+ },
+ "scripts": {
+ "lint": "parallel-lint . --exclude vendor",
+ "phpunit": "phpunit",
+ "phpunit-unit": "phpunit --testsuite unit",
+ "phpunit-integration": "phpunit --testsuite integration",
+ "phpunit-coverage": "phpunit --coverage-clover=coverage.clover",
+ "phpcs": "phpcs -ps",
+ "test": [
+ "@lint",
+ "@phpcs",
+ "@phpunit"
+ ]
+ },
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "files": [
+ "tests/Integration/TestEnvironment.php"
+ ]
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.4.x-dev"
+ }
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/Makefile b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/Makefile
new file mode 100644
index 00000000..54002b3b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/Makefile
@@ -0,0 +1,225 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " epub3 to make an epub3"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+ @echo " dummy to check syntax errors of document sources"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mediawiki-api-base.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mediawiki-api-base.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/mediawiki-api-base"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mediawiki-api-base"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: epub3
+epub3:
+ $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+ @echo
+ @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+ $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+ @echo
+ @echo "Build finished. Dummy builder generates no files."
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/conf.py b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/conf.py
new file mode 100644
index 00000000..cb3ce592
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/conf.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+import sys, os
+from sphinx.highlighting import lexers
+from pygments.lexers.web import PhpLexer
+
+lexers['php'] = PhpLexer(startinline=True, linenos=1)
+lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1)
+primary_domain = 'php'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'mediawiki-api-base'
+copyright = '2016, addwiki'
+author = 'addwiki'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '2.2'
+# The full version, including alpha/beta/rc tags.
+release = '2.2.1'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'default'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'mediawiki-api-basedoc'
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/index.rst b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/index.rst
new file mode 100644
index 00000000..ac4860ce
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/index.rst
@@ -0,0 +1,17 @@
+Welcome to mediawiki-api-base's documentation!
+==============================================
+
+.. toctree::
+ :maxdepth: 3
+
+ overview
+ quickstart
+ multipart
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/make.bat b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/make.bat
new file mode 100644
index 00000000..7ce5e397
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/make.bat
@@ -0,0 +1,281 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. epub3 to make an epub3
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ echo. dummy to check syntax errors of document sources
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\mediawiki-api-base.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\mediawiki-api-base.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "epub3" (
+ %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+if "%1" == "dummy" (
+ %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. Dummy builder generates no files.
+ goto end
+)
+
+:end
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/multipart.rst b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/multipart.rst
new file mode 100644
index 00000000..1b438c71
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/multipart.rst
@@ -0,0 +1,28 @@
+==================
+Multipart requests
+==================
+
+The MultipartRequest class can be used if you need a FluentRequest that has more parameters to be set on individual parts of a multipart request.
+
+The name is a slight misnomer, because either of the other two Request classes (SimpleRequest and FluentRequest)
+will also end up being multipart requests if you pass any parameters of type Resource_.
+
+.. _Resource: http://php.net/manual/en/resource.php
+
+To use a MultipartRequest you must first set the main parameters, and then you can add additional "multipart parameters" to any of the parameters you've set.
+(You will get an Exception if you try to set a multipart parameter for a main parameter that doesn't exist yet.)
+
+For example, to add a ``Content-Disposition`` header to a parameter named ``param1``::
+
+ $contentDisposition = 'form-data; name="param1"; filename="a_filename.png"';
+ $request = MultipartRequest::factory()
+ ->setParams( [ 'param1' => 'Lorem ipsum' ] )
+ ->setAction( 'actionname' )
+ ->setMultipartParams( [
+ 'param1' => [
+ 'headers' => [ 'Content-Disposition' => $contentDisposition ],
+ ],
+ ] );
+ $response = $api->postRequest( $request );
+
+(For details of creating the ``$api`` object in this example, see :ref:`quickstart`.)
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/overview.rst b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/overview.rst
new file mode 100644
index 00000000..0e83549d
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/overview.rst
@@ -0,0 +1,129 @@
+========
+Overview
+========
+
+addwiki/mediawiki-api-base is a PHP HTTP client wrapped around guzzle that makes it easy to interest with a mediawiki installation.
+
+#. Uses PSR-3 interfaces for logging
+#. Handles Mediawiki login, sessions, cookies and tokens
+#. Handles response errors by throwing catchable UsageExceptions
+#. Retries failed requests where possible
+#. Allows Async requests
+
+Requirements
+========================
+
+#. PHP 5.5.0
+#. Guzzle HTTP library ~6.0
+
+.. _installation:
+
+Installation
+========================
+
+The recommended way to install this library is with
+`Composer <http://getcomposer.org>`_. Composer is a dependency management tool
+for PHP that allows you to declare the dependencies your project needs and
+installs them into your project.
+
+.. code-block:: bash
+
+ # Install Composer
+ curl -sS https://getcomposer.org/installer | php
+
+You can add addwiki/mediawiki-api-base as a dependency using the composer.phar CLI:
+
+.. code-block:: bash
+
+ php composer.phar require addwiki/mediawiki-api-base:~2.0
+
+Alternatively, you can specify addwiki/mediawiki-api-base as a dependency in your project's
+existing composer.json file:
+
+.. code-block:: js
+
+ {
+ "require": {
+ "addwiki/mediawiki-api-base": "~2.0"
+ }
+ }
+
+After installing, you need to require Composer's autoloader:
+
+.. code-block:: php
+
+ require 'vendor/autoload.php';
+
+You can find out more on how to install Composer, configure autoloading, and
+other best-practices for defining dependencies at `getcomposer.org <http://getcomposer.org>`_.
+
+
+Bleeding edge
+--------------------------
+
+During your development, you can keep up with the latest changes on the master
+branch by setting the version requirement for addwiki/mediawiki-api-base to ``~2.0@dev``.
+
+.. code-block:: js
+
+ {
+ "require": {
+ "addwiki/mediawiki-api-base": "~2.0@dev"
+ }
+ }
+
+
+License
+===================
+
+Licensed using the `GPL-2.0+ <https://opensource.org/licenses/GPL-2.0>`_.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+Contributing
+========================
+
+Running the tests
+-----------------
+
+In order to contribute, you'll need to checkout the source from GitHub and
+install the dependencies using Composer:
+
+.. code-block:: bash
+
+ git clone https://github.com/addwiki/mediawiki-api-base.git
+ cd mediawiki-api-base
+ curl -s http://getcomposer.org/installer | php
+ ./composer.phar install --dev
+
+The library is tested with a combination of linters and phpunit. Run all of the tests as follows:
+
+.. code-block:: bash
+
+ ./composer.phar test
+
+You can choose to run each part of the whole test suite individually using the following commands:
+
+.. code-block:: bash
+
+ # Run the linting only
+ ./composer.phar lint
+ # Run phpunit only
+ ./composer.phar phpunit
+ # Run only the phpunit unit tests
+ ./composer.phar phpunit-unit
+ # Run only the phpunit integration tests
+ ./composer.phar phpunit-integration \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/quickstart.rst b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/quickstart.rst
new file mode 100644
index 00000000..5db0601f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/docs/quickstart.rst
@@ -0,0 +1,87 @@
+.. _quickstart:
+
+==========
+Quickstart
+==========
+
+This page provides a quick introduction to this library and introductory examples.
+If you have not already installed the library head over to the :ref:`installation`
+page.
+
+Getting an API object
+----------------------------------
+
+You can get an api object by simply passing the api endpoint:
+
+.. code-block:: php
+
+ use \Mediawiki\Api\MediawikiApi;
+
+ $api = MediawikiApi::newFromApiEndpoint( 'https://en.wikipedia.org/w/api.php' );
+
+You can even just pass a page:
+
+.. code-block:: php
+
+ use \Mediawiki\Api\MediawikiApi;
+
+ $api = MediawikiApi::newFromPage( 'https://en.wikipedia.org/wiki/Berlin' );
+
+Logging in and out
+----------------------------------
+
+.. code-block:: php
+
+ use \MediawikiApi\Api\ApiUser;
+
+ $api->login( new ApiUser( 'username', 'password' ) );
+ $api->logout();
+
+Making request objects
+----------------------------------
+
+The library provides two different way of constructing requests.
+
+.. code-block:: php
+
+ use Mediawiki\Api\SimpleRequest;
+ use Mediawiki\Api\FluentRequest;
+
+ $purgeRequest = new SimpleRequest( 'purge', array( 'titles' => 'Berlin' ) );
+ // or
+ $purgeRequest = FluentRequest::factory()->setAction( 'purge' )->setParam( 'titles', 'Berlin' ) );
+
+Sending requests
+----------------------------------
+
+.. code-block:: php
+
+ $api->postRequest( $purgeRequest );
+
+ $queryResponse = $api->getRequest( FluentRequest::factory()->setAction( 'query' )->setParam( 'meta', 'siteinfo' ) );
+
+ try{
+ $api->postRequest( new SimpleRequest( 'FooBarBaz' ) );
+ }
+ catch ( UsageException $e ) {
+ echo "The api returned an error!";
+ }
+
+
+Making async requests
+----------------------------------
+
+.. code-block:: php
+
+ // Initiate each request but do not block
+ $requestPromises = array(
+ 'Page1' => $api->postRequestAsync( FluentRequest::factory()->setAction( 'purge' )->setParam( 'titles', 'Page1' ) ),
+ 'Page2' => $api->postRequestAsync( FluentRequest::factory()->setAction( 'purge' )->setParam( 'titles', 'Page2' ) ),
+ 'Page3' => $api->postRequestAsync( FluentRequest::factory()->setAction( 'purge' )->setParam( 'titles', 'Page3' ) ),
+ );
+
+ // Wait on all of the requests to complete.
+ $results = GuzzleHttp\Promise\unwrap( $requestPromises );
+
+ // You can access each result using the key provided to the unwrap function.
+ print_r( $results['Page1'], $results['Page2'], $results['Page3'] )
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/phpcs.xml b/bin/wiki/vendor/addwiki/mediawiki-api-base/phpcs.xml
new file mode 100755
index 00000000..c4ca4081
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/phpcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<ruleset name="MediaWiki">
+ <rule ref="vendor/mediawiki/mediawiki-codesniffer/MediaWiki"/>
+ <file>.</file>
+ <exclude-pattern>vendor/</exclude-pattern>
+</ruleset>
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api-base/phpunit.xml.dist
new file mode 100644
index 00000000..a5874032
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/phpunit.xml.dist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit colors="true">
+ <testsuites>
+ <testsuite name="unit">
+ <directory suffix="Test.php">./tests/Unit</directory>
+ </testsuite>
+ <testsuite name="integration">
+ <directory suffix="Test.php">./tests/Integration</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./src</directory>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php
new file mode 100644
index 00000000..cc3c8bcf
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiRequester.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Mediawiki\Api;
+
+/**
+ * @since 2.2
+ * @licence GNU GPL v2+
+ * @author Addshore
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface ApiRequester {
+
+ /**
+ * @since 2.2
+ *
+ * @param Request $request The GET request to send.
+ *
+ * @return mixed Normally an array
+ */
+ public function getRequest( Request $request );
+
+ /**
+ * @since 2.2
+ *
+ * @param Request $request The POST request to send.
+ *
+ * @return mixed Normally an array
+ */
+ public function postRequest( Request $request );
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiUser.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiUser.php
new file mode 100644
index 00000000..b2f59153
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/ApiUser.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use InvalidArgumentException;
+
+/**
+ * @since 0.1
+ *
+ * @author Addshore
+ * @author RobinR1
+ * @author Bene
+ *
+ * Represents a user that can log in to the api
+ */
+class ApiUser {
+
+ /**
+ * @var string
+ */
+ private $password;
+
+ /**
+ * @var string
+ */
+ private $username;
+
+ /**
+ * @var string
+ */
+ private $domain;
+
+ /**
+ * @param string $username The username.
+ * @param string $password The user's password.
+ * @param string|null $domain The domain (for authentication systems that support domains).
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct( $username, $password, $domain = null ) {
+ $domainIsStringOrNull = ( is_string( $domain ) || is_null( $domain ) );
+ if ( !is_string( $username ) || !is_string( $password ) || !$domainIsStringOrNull ) {
+ throw new InvalidArgumentException( 'Username, Password and Domain must all be strings' );
+ }
+ if ( empty( $username ) || empty( $password ) ) {
+ throw new InvalidArgumentException( 'Username and Password are not allowed to be empty' );
+ }
+ $this->username = $username;
+ $this->password = $password;
+ $this->domain = $domain;
+ }
+
+ /**
+ * @since 0.1
+ * @return string
+ */
+ public function getUsername() {
+ return $this->username;
+ }
+
+ /**
+ * @since 0.1
+ * @return string
+ */
+ public function getPassword() {
+ return $this->password;
+ }
+
+ /**
+ * @since 0.1
+ * @return string
+ */
+ public function getDomain() {
+ return $this->domain;
+ }
+
+ /**
+ * @since 0.1
+ * @param mixed $other Another ApiUser object to compare with.
+ *
+ * @return bool
+ */
+ public function equals( $other ) {
+ return $other instanceof self
+ && $this->username == $other->getUsername()
+ && $this->password == $other->getPassword()
+ && $this->domain == $other->getDomain();
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php
new file mode 100644
index 00000000..6190ac7e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/AsyncApiRequester.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use GuzzleHttp\Promise\PromiseInterface;
+
+/**
+ * @since 2.2
+ * @licence GNU GPL v2+
+ * @author Addshore
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface AsyncApiRequester {
+
+ /**
+ * @since 2.2
+ *
+ * @param Request $request The GET request to send.
+ *
+ * @return PromiseInterface
+ * Normally promising an array, though can be mixed (json_decode result)
+ * Can throw UsageExceptions or RejectionExceptions
+ */
+ public function getRequestAsync( Request $request );
+
+ /**
+ * @since 2.2
+ *
+ * @param Request $request The POST request to send.
+ *
+ * @return PromiseInterface
+ * Normally promising an array, though can be mixed (json_decode result)
+ * Can throw UsageExceptions or RejectionExceptions
+ */
+ public function postRequestAsync( Request $request );
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php
new file mode 100644
index 00000000..0d10553b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/FluentRequest.php
@@ -0,0 +1,118 @@
+<?php
+
+namespace Mediawiki\Api;
+
+/**
+ * @since 1.0
+ *
+ * @author Addshore
+ */
+class FluentRequest implements Request {
+
+ /**
+ * @var array
+ */
+ private $params = [];
+
+ /**
+ * @var array
+ */
+ private $headers = [];
+
+ /**
+ * @since 1.0
+ *
+ * @return array
+ */
+ public function getParams() {
+ return $this->params;
+ }
+
+ /**
+ * @since 1.0
+ *
+ * @return array
+ */
+ public function getHeaders() {
+ return $this->headers;
+ }
+
+ /**
+ * @since 1.0
+ *
+ * @return static
+ */
+ public static function factory() {
+ return new static();
+ }
+
+ /**
+ * @since 1.0
+ *
+ * @param string $action The action name.
+ *
+ * @return $this
+ */
+ public function setAction( $action ) {
+ $this->setParam( 'action', $action );
+ return $this;
+ }
+
+ /**
+ * Totally overwrite any previously set params
+ *
+ * @since 1.0
+ *
+ * @param array $params New parameters.
+ *
+ * @return $this
+ */
+ public function setParams( array $params ) {
+ $this->params = $params;
+ return $this;
+ }
+
+ /**
+ * Totally overwrite any previously set params
+ *
+ * @since 1.0
+ *
+ * @param array $params Additional parameters.
+ *
+ * @return $this
+ */
+ public function addParams( array $params ) {
+ $this->params = array_merge( $this->params, $params );
+ return $this;
+ }
+
+ /**
+ * Set a single parameter.
+ *
+ * @since 1.0
+ *
+ * @param string $param The parameter name.
+ * @param string $value The parameter value.
+ *
+ * @return $this
+ */
+ public function setParam( $param, $value ) {
+ $this->params[$param] = $value;
+ return $this;
+ }
+
+ /**
+ * Totally overwrite any previously set HTTP headers.
+ *
+ * @since 1.0
+ *
+ * @param array $headers New headers.
+ *
+ * @return $this
+ */
+ public function setHeaders( $headers ) {
+ $this->headers = $headers;
+ return $this;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php
new file mode 100644
index 00000000..704a1660
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/ClientFactory.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Mediawiki\Api\Guzzle;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Handler\CurlHandler;
+use GuzzleHttp\HandlerStack;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+/**
+ * @since 2.1
+ *
+ * @author Addshore
+ */
+class ClientFactory implements LoggerAwareInterface {
+
+ private $client;
+ private $logger;
+ private $config;
+
+ /**
+ * @since 2.1
+ *
+ * @param array $config All configuration settings supported by Guzzle, and these:
+ * middleware => array of extra middleware to pass to guzzle
+ * user-agent => string default user agent to use for requests
+ */
+ public function __construct( array $config = [] ) {
+ $this->logger = new NullLogger();
+ $this->config = $config;
+ }
+
+ /**
+ * @since 2.1
+ *
+ * @return Client
+ */
+ public function getClient() {
+ if ( $this->client === null ) {
+ $this->client = $this->newClient();
+ }
+ return $this->client;
+ }
+
+ /**
+ * @return Client
+ */
+ private function newClient() {
+ $this->config += [
+ 'cookies' => true,
+ 'headers' => [],
+ 'middleware' => [],
+ ];
+
+ if ( !array_key_exists( 'User-Agent', $this->config['headers'] ) ) {
+ if ( array_key_exists( 'user-agent', $this->config ) ) {
+ $this->config['headers']['User-Agent'] = $this->config['user-agent'];
+ } else {
+ $this->config['headers']['User-Agent'] = 'Addwiki - mediawiki-api-base';
+ }
+ }
+ unset( $this->config['user-agent'] );
+
+ if ( !array_key_exists( 'handler', $this->config ) ) {
+ $this->config['handler'] = HandlerStack::create( new CurlHandler() );
+ }
+
+ $middlewareFactory = new MiddlewareFactory();
+ $middlewareFactory->setLogger( $this->logger );
+
+ $this->config['middleware'][] = $middlewareFactory->retry();
+
+ foreach ( $this->config['middleware'] as $name => $middleware ) {
+ $this->config['handler']->push( $middleware );
+ }
+ unset( $this->config['middleware'] );
+
+ return new Client( $this->config );
+ }
+
+ /**
+ * Sets a logger instance on the object
+ *
+ * @since 2.1
+ *
+ * @param LoggerInterface $logger The new Logger object.
+ *
+ * @return null
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php
new file mode 100644
index 00000000..9e03c02f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Guzzle/MiddlewareFactory.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace Mediawiki\Api\Guzzle;
+
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Middleware;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\Psr7\Response;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class MiddlewareFactory implements LoggerAwareInterface {
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ public function __construct() {
+ $this->logger = new NullLogger();
+ }
+
+ /**
+ * @param LoggerInterface $logger The new Logger object.
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * @access private
+ *
+ * @param bool $delay default to true, can be false to speed up tests
+ *
+ * @return callable
+ */
+ public function retry( $delay = true ) {
+ if ( $delay ) {
+ return Middleware::retry( $this->newRetryDecider(), $this->getRetryDelay() );
+ } else {
+ return Middleware::retry( $this->newRetryDecider() );
+ }
+ }
+
+ /**
+ * Returns a method that takes the number of retries and returns the number of miliseconds
+ * to wait
+ *
+ * @return callable
+ */
+ private function getRetryDelay() {
+ return function ( $numberOfRetries, Response $response = null ) {
+ // The $response argument is only passed as of Guzzle 6.2.2.
+ if ( $response !== null ) {
+ // Retry-After may be a number of seconds or an absolute date (RFC 7231,
+ // section 7.1.3).
+ $retryAfter = $response->getHeaderLine( 'Retry-After' );
+
+ if ( is_numeric( $retryAfter ) ) {
+ return 1000 * $retryAfter;
+ }
+
+ if ( $retryAfter ) {
+ $seconds = strtotime( $retryAfter ) - time();
+ return 1000 * max( 1, $seconds );
+ }
+ }
+
+ return 1000 * $numberOfRetries;
+ };
+ }
+
+ /**
+ * @return callable
+ */
+ private function newRetryDecider() {
+ return function (
+ $retries,
+ Request $request,
+ Response $response = null,
+ RequestException $exception = null
+ ) {
+ // Don't retry if we have run out of retries
+ if ( $retries >= 5 ) {
+ return false;
+ }
+
+ $shouldRetry = false;
+
+ // Retry connection exceptions
+ if ( $exception instanceof ConnectException ) {
+ $shouldRetry = true;
+ }
+
+ if ( $response ) {
+ $data = json_decode( $response->getBody(), true );
+
+ // Retry on server errors
+ if ( $response->getStatusCode() >= 500 ) {
+ $shouldRetry = true;
+ }
+
+ foreach ( $response->getHeader( 'Mediawiki-Api-Error' ) as $mediawikiApiErrorHeader ) {
+ if (
+ // Retry if the API explicitly tells us to:
+ // https://www.mediawiki.org/wiki/Manual:Maxlag_parameter
+ $response->getHeaderLine( 'Retry-After' )
+ ||
+ // Retry if we have a response with an API error worth retrying
+ in_array(
+ $mediawikiApiErrorHeader,
+ [
+ 'ratelimited',
+ 'maxlag',
+ 'readonly',
+ 'internal_api_error_DBQueryError',
+ ]
+ )
+ ||
+ // Or if we have been stopped from saving as an 'anti-abuse measure'
+ // Note: this tries to match "actionthrottledtext" i18n messagae for mediawiki
+ (
+ $mediawikiApiErrorHeader == 'failed-save' &&
+ strstr( $data['error']['info'], 'anti-abuse measure' )
+ )
+ ) {
+ $shouldRetry = true;
+ }
+
+ }
+ }
+
+ // Log if we are retrying
+ if ( $shouldRetry ) {
+ $this->logger->warning(
+ sprintf(
+ 'Retrying %s %s %s/5, %s',
+ $request->getMethod(),
+ $request->getUri(),
+ $retries + 1,
+ $response ? 'status code: ' . $response->getStatusCode() :
+ $exception->getMessage()
+ )
+ );
+ }
+
+ return $shouldRetry;
+ };
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php
new file mode 100644
index 00000000..0c6c4fe3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApi.php
@@ -0,0 +1,507 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use DOMDocument;
+use DOMXPath;
+use GuzzleHttp\Client;
+use GuzzleHttp\ClientInterface;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Promise\PromiseInterface;
+use InvalidArgumentException;
+use Mediawiki\Api\Guzzle\ClientFactory;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+use Psr\Log\NullLogger;
+use SimpleXMLElement;
+
+/**
+ * Main class for this library
+ *
+ * @since 0.1
+ *
+ * @author Addshore
+ */
+class MediawikiApi implements MediawikiApiInterface, LoggerAwareInterface {
+
+ /**
+ * @var ClientInterface|null Should be accessed through getClient
+ */
+ private $client = null;
+
+ /**
+ * @var bool|string
+ */
+ private $isLoggedIn;
+
+ /**
+ * @var MediawikiSession
+ */
+ private $session;
+
+ /**
+ * @var string
+ */
+ private $version;
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ /**
+ * @var string
+ */
+ private $apiUrl;
+
+ /**
+ * @since 2.0
+ *
+ * @param string $apiEndpoint e.g. https://en.wikipedia.org/w/api.php
+ *
+ * @return self returns a MediawikiApi instance using $apiEndpoint
+ */
+ public static function newFromApiEndpoint( $apiEndpoint ) {
+ return new self( $apiEndpoint );
+ }
+
+ /**
+ * Create a new MediawikiApi object from a URL to any page in a MediaWiki website.
+ *
+ * @since 2.0
+ * @see https://en.wikipedia.org/wiki/Really_Simple_Discovery
+ *
+ * @param string $url e.g. https://en.wikipedia.org OR https://de.wikipedia.org/wiki/Berlin
+ * @return self returns a MediawikiApi instance using the apiEndpoint provided by the RSD
+ * file accessible on all Mediawiki pages
+ * @throws RsdException If the RSD URL could not be found in the page's HTML.
+ */
+ public static function newFromPage( $url ) {
+ // Set up HTTP client and HTML document.
+ $tempClient = new Client( [ 'headers' => [ 'User-Agent' => 'addwiki-mediawiki-client' ] ] );
+ $pageHtml = $tempClient->get( $url )->getBody();
+ $pageDoc = new DOMDocument();
+
+ // Try to load the HTML (turn off errors temporarily; most don't matter, and if they do get
+ // in the way of finding the API URL, will be reported in the RsdException below).
+ $internalErrors = libxml_use_internal_errors( true );
+ $pageDoc->loadHTML( $pageHtml );
+ $libXmlErrors = libxml_get_errors();
+ libxml_use_internal_errors( $internalErrors );
+
+ // Extract the RSD link.
+ $xpath = 'head/link[@type="application/rsd+xml"][@href]';
+ $link = ( new DOMXpath( $pageDoc ) )->query( $xpath );
+ if ( $link->length === 0 ) {
+ // Format libxml errors for display.
+ $libXmlErrorStr = array_reduce( $libXmlErrors, function ( $prevErr, $err ) {
+ return $prevErr . ', ' . $err->message . ' (line '.$err->line . ')';
+ } );
+ if ( $libXmlErrorStr ) {
+ $libXmlErrorStr = "In addition, libxml had the following errors: $libXmlErrorStr";
+ }
+ throw new RsdException( "Unable to find RSD URL in page: $url $libXmlErrorStr" );
+ }
+ $rsdUrl = $link->item( 0 )->attributes->getnamedItem( 'href' )->nodeValue;
+
+ // Then get the RSD XML, and return the API link.
+ $rsdXml = new SimpleXMLElement( $tempClient->get( $rsdUrl )->getBody() );
+ return self::newFromApiEndpoint( (string)$rsdXml->service->apis->api->attributes()->apiLink );
+ }
+
+ /**
+ * @param string $apiUrl The API Url
+ * @param ClientInterface|null $client Guzzle Client
+ * @param MediawikiSession|null $session Inject a custom session here
+ */
+ public function __construct( $apiUrl, ClientInterface $client = null,
+ MediawikiSession $session = null ) {
+ if ( !is_string( $apiUrl ) ) {
+ throw new InvalidArgumentException( '$apiUrl must be a string' );
+ }
+ if ( $session === null ) {
+ $session = new MediawikiSession( $this );
+ }
+
+ $this->apiUrl = $apiUrl;
+ $this->client = $client;
+ $this->session = $session;
+
+ $this->logger = new NullLogger();
+ }
+
+ /**
+ * Get the API URL (the URL to which API requests are sent, usually ending in api.php).
+ * This is useful if you've created this object via MediawikiApi::newFromPage().
+ *
+ * @since 2.3
+ *
+ * @return string The API URL.
+ */
+ public function getApiUrl() {
+ return $this->apiUrl;
+ }
+
+ /**
+ * @return ClientInterface
+ */
+ private function getClient() {
+ if ( $this->client === null ) {
+ $clientFactory = new ClientFactory();
+ $clientFactory->setLogger( $this->logger );
+ $this->client = $clientFactory->getClient();
+ }
+ return $this->client;
+ }
+
+ /**
+ * Sets a logger instance on the object
+ *
+ * @since 1.1
+ *
+ * @param LoggerInterface $logger The new Logger object.
+ *
+ * @return null
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ $this->session->setLogger( $logger );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Request $request The GET request to send.
+ *
+ * @return PromiseInterface
+ * Normally promising an array, though can be mixed (json_decode result)
+ * Can throw UsageExceptions or RejectionExceptions
+ */
+ public function getRequestAsync( Request $request ) {
+ $promise = $this->getClient()->requestAsync(
+ 'GET',
+ $this->apiUrl,
+ $this->getClientRequestOptions( $request, 'query' )
+ );
+
+ return $promise->then( function ( ResponseInterface $response ) {
+ return call_user_func( [ $this, 'decodeResponse' ], $response );
+ } );
+ }
+
+ /**
+ * @since 2.0
+ *
+ * @param Request $request The POST request to send.
+ *
+ * @return PromiseInterface
+ * Normally promising an array, though can be mixed (json_decode result)
+ * Can throw UsageExceptions or RejectionExceptions
+ */
+ public function postRequestAsync( Request $request ) {
+ $promise = $this->getClient()->requestAsync(
+ 'POST',
+ $this->apiUrl,
+ $this->getClientRequestOptions( $request, $this->getPostRequestEncoding( $request ) )
+ );
+
+ return $promise->then( function ( ResponseInterface $response ) {
+ return call_user_func( [ $this, 'decodeResponse' ], $response );
+ } );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Request $request The GET request to send.
+ *
+ * @return mixed Normally an array
+ */
+ public function getRequest( Request $request ) {
+ $response = $this->getClient()->request(
+ 'GET',
+ $this->apiUrl,
+ $this->getClientRequestOptions( $request, 'query' )
+ );
+
+ return $this->decodeResponse( $response );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Request $request The POST request to send.
+ *
+ * @return mixed Normally an array
+ */
+ public function postRequest( Request $request ) {
+ $response = $this->getClient()->request(
+ 'POST',
+ $this->apiUrl,
+ $this->getClientRequestOptions( $request, $this->getPostRequestEncoding( $request ) )
+ );
+
+ return $this->decodeResponse( $response );
+ }
+
+ /**
+ * @param ResponseInterface $response
+ *
+ * @return mixed
+ * @throws UsageException
+ */
+ private function decodeResponse( ResponseInterface $response ) {
+ $resultArray = json_decode( $response->getBody(), true );
+
+ $this->logWarnings( $resultArray );
+ $this->throwUsageExceptions( $resultArray );
+
+ return $resultArray;
+ }
+
+ /**
+ * @param Request $request
+ *
+ * @return string
+ */
+ private function getPostRequestEncoding( Request $request ) {
+ if ( $request instanceof MultipartRequest ) {
+ return 'multipart';
+ }
+ foreach ( $request->getParams() as $value ) {
+ if ( is_resource( $value ) ) {
+ return 'multipart';
+ }
+ }
+ return 'form_params';
+ }
+
+ /**
+ * @param Request $request
+ * @param string $paramsKey either 'query' or 'multipart'
+ *
+ * @throws RequestException
+ *
+ * @return array as needed by ClientInterface::get and ClientInterface::post
+ */
+ private function getClientRequestOptions( Request $request, $paramsKey ) {
+ $params = array_merge( $request->getParams(), [ 'format' => 'json' ] );
+ if ( $paramsKey === 'multipart' ) {
+ $params = $this->encodeMultipartParams( $request, $params );
+ }
+
+ return [
+ $paramsKey => $params,
+ 'headers' => array_merge( $this->getDefaultHeaders(), $request->getHeaders() ),
+ ];
+ }
+
+ /**
+ * Turn the normal key-value array of request parameters into a multipart array where each
+ * parameter is a new array with a 'name' and 'contents' elements (and optionally more, if the
+ * request is a MultipartRequest).
+ *
+ * @param Request $request The request to which the parameters belong.
+ * @param string[] $params The existing parameters. Not the same as $request->getParams().
+ *
+ * @return array
+ */
+ private function encodeMultipartParams( Request $request, $params ) {
+ // See if there are any multipart parameters in this request.
+ $multipartParams = ( $request instanceof MultipartRequest )
+ ? $request->getMultipartParams()
+ : [];
+ return array_map(
+ function ( $name, $value ) use ( $multipartParams ) {
+ $partParams = [
+ 'name' => $name,
+ 'contents' => $value,
+ ];
+ if ( isset( $multipartParams[ $name ] ) ) {
+ // If extra parameters have been set for this part, use them.
+ $partParams = array_merge( $multipartParams[ $name ], $partParams );
+ }
+ return $partParams;
+ },
+ array_keys( $params ),
+ $params
+ );
+ }
+
+ /**
+ * @return array
+ */
+ private function getDefaultHeaders() {
+ return [
+ 'User-Agent' => $this->getUserAgent(),
+ ];
+ }
+
+ private function getUserAgent() {
+ $loggedIn = $this->isLoggedin();
+ if ( $loggedIn ) {
+ return 'addwiki-mediawiki-client/' . $loggedIn;
+ }
+ return 'addwiki-mediawiki-client';
+ }
+
+ /**
+ * @param $result
+ */
+ private function logWarnings( $result ) {
+ if ( is_array( $result ) && array_key_exists( 'warnings', $result ) ) {
+ foreach ( $result['warnings'] as $module => $warningData ) {
+ // Accomodate both formatversion=2 and old-style API results
+ $logPrefix = $module . ': ';
+ if ( isset( $warningData['*'] ) ) {
+ $this->logger->warning( $logPrefix . $warningData['*'], [ 'data' => $warningData ] );
+ } else {
+ $this->logger->warning( $logPrefix . $warningData['warnings'], [ 'data' => $warningData ] );
+ }
+ }
+ }
+ }
+
+ /**
+ * @param array $result
+ *
+ * @throws UsageException
+ */
+ private function throwUsageExceptions( $result ) {
+ if ( is_array( $result ) && array_key_exists( 'error', $result ) ) {
+ throw new UsageException(
+ $result['error']['code'],
+ $result['error']['info'],
+ $result
+ );
+ }
+ }
+
+ /**
+ * @since 0.1
+ *
+ * @return bool|string false or the name of the current user
+ */
+ public function isLoggedin() {
+ return $this->isLoggedIn;
+ }
+
+ /**
+ * @since 0.1
+ *
+ * @param ApiUser $apiUser The ApiUser to log in as.
+ *
+ * @throws UsageException
+ * @return bool success
+ */
+ public function login( ApiUser $apiUser ) {
+ $this->logger->log( LogLevel::DEBUG, 'Logging in' );
+ $credentials = $this->getLoginParams( $apiUser );
+ $result = $this->postRequest( new SimpleRequest( 'login', $credentials ) );
+ if ( $result['login']['result'] == "NeedToken" ) {
+ $params = array_merge( [ 'lgtoken' => $result['login']['token'] ], $credentials );
+ $result = $this->postRequest( new SimpleRequest( 'login', $params ) );
+ }
+ if ( $result['login']['result'] == "Success" ) {
+ $this->isLoggedIn = $apiUser->getUsername();
+ return true;
+ }
+
+ $this->isLoggedIn = false;
+ $this->logger->log( LogLevel::DEBUG, 'Login failed.', $result );
+ $this->throwLoginUsageException( $result );
+ return false;
+ }
+
+ /**
+ * @param ApiUser $apiUser
+ *
+ * @return string[]
+ */
+ private function getLoginParams( ApiUser $apiUser ) {
+ $params = [
+ 'lgname' => $apiUser->getUsername(),
+ 'lgpassword' => $apiUser->getPassword(),
+ ];
+
+ if ( !is_null( $apiUser->getDomain() ) ) {
+ $params['lgdomain'] = $apiUser->getDomain();
+ }
+ return $params;
+ }
+
+ /**
+ * @param array $result
+ *
+ * @throws UsageException
+ */
+ private function throwLoginUsageException( $result ) {
+ $loginResult = $result['login']['result'];
+
+ throw new UsageException(
+ 'login-' . $loginResult,
+ array_key_exists( 'reason', $result['login'] )
+ ? $result['login']['reason']
+ : 'No Reason given',
+ $result
+ );
+ }
+
+ /**
+ * @since 0.1
+ *
+ * @return bool success
+ */
+ public function logout() {
+ $this->logger->log( LogLevel::DEBUG, 'Logging out' );
+ $result = $this->postRequest( new SimpleRequest( 'logout' ) );
+ if ( $result === [] ) {
+ $this->isLoggedIn = false;
+ $this->clearTokens();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @since 0.1
+ *
+ * @param string $type The token type to get.
+ *
+ * @return string
+ */
+ public function getToken( $type = 'csrf' ) {
+ return $this->session->getToken( $type );
+ }
+
+ /**
+ * Clear all tokens stored by the API.
+ *
+ * @since 0.1
+ */
+ public function clearTokens() {
+ $this->session->clearTokens();
+ }
+
+ /**
+ * @return string
+ */
+ public function getVersion() {
+ if ( !isset( $this->version ) ) {
+ $result = $this->getRequest( new SimpleRequest( 'query', [
+ 'meta' => 'siteinfo',
+ 'continue' => '',
+ ] ) );
+ preg_match(
+ '/\d+(?:\.\d+)+/',
+ $result['query']['general']['generator'],
+ $versionParts
+ );
+ $this->version = $versionParts[0];
+ }
+ return $this->version;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php
new file mode 100644
index 00000000..83580676
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiApiInterface.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Mediawiki\Api;
+
+/**
+ * @since 2.2
+ * @licence GNU GPL v2+
+ * @author Addshore
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+interface MediawikiApiInterface extends ApiRequester, AsyncApiRequester {
+
+ /**
+ * @since 2.2
+ *
+ * @return bool|string false or the name of the current user
+ */
+ public function isLoggedin();
+
+ /**
+ * @since 2.2
+ *
+ * @param ApiUser $apiUser The ApiUser to log in as.
+ *
+ * @throws UsageException
+ * @return bool success
+ */
+ public function login( ApiUser $apiUser );
+
+ /**
+ * @since 2.2
+ *
+ * @return bool success
+ */
+ public function logout();
+
+ /**
+ * @since 2.2
+ *
+ * @param string $type The type of token to get.
+ *
+ * @return string
+ */
+ public function getToken( $type = 'csrf' );
+
+ /**
+ * @since 2.2
+ *
+ * Clears all tokens stored by the api
+ */
+ public function clearTokens();
+
+ /**
+ * @since 2.2
+ *
+ * @return string
+ */
+ public function getVersion();
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php
new file mode 100644
index 00000000..c430695c
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MediawikiSession.php
@@ -0,0 +1,168 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+use Psr\Log\NullLogger;
+
+/**
+ * @since 0.1
+ *
+ * @author Addshore
+ */
+class MediawikiSession implements LoggerAwareInterface {
+
+ /**
+ * @var array
+ */
+ private $tokens = [];
+
+ /**
+ * @var MediawikiApi
+ */
+ private $api;
+
+ /**
+ * @var bool if this session is running against mediawiki version pre 1.25
+ */
+ private $usePre125TokensModule = false;
+
+ /**
+ * @var LoggerInterface
+ */
+ private $logger;
+
+ /**
+ * @param MediawikiApi $api The API object to use for this session.
+ */
+ public function __construct( MediawikiApi $api ) {
+ $this->api = $api;
+ $this->logger = new NullLogger();
+ }
+
+ /**
+ * Sets a logger instance on the object
+ *
+ * @since 1.1
+ *
+ * @param LoggerInterface $logger The new Logger object.
+ *
+ * @return null
+ */
+ public function setLogger( LoggerInterface $logger ) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * Tries to get the specified token from the API
+ *
+ * @since 0.1
+ *
+ * @param string $type The type of token to get.
+ *
+ * @return string
+ */
+ public function getToken( $type = 'csrf' ) {
+ // If we don't already have the token that we want
+ if ( !array_key_exists( $type, $this->tokens ) ) {
+ $this->logger->log( LogLevel::DEBUG, 'Getting fresh token', [ 'type' => $type ] );
+
+ // If we know that we don't have the new module mw<1.25
+ if ( $this->usePre125TokensModule ) {
+ return $this->reallyGetPre125Token( $type );
+ } else {
+ return $this->reallyGetToken( $type );
+ }
+
+ }
+
+ return $this->tokens[$type];
+ }
+
+ private function reallyGetPre125Token( $type ) {
+ // Suppress deprecation warning
+ $result = @$this->api->postRequest( // @codingStandardsIgnoreLine
+ new SimpleRequest( 'tokens', [ 'type' => $this->getOldTokenType( $type ) ] )
+ );
+ $this->tokens[$type] = array_pop( $result['tokens'] );
+
+ return $this->tokens[$type];
+ }
+
+ private function reallyGetToken( $type ) {
+ // We suppress errors on this call so the user doesn't get get a warning that isn't their fault.
+ $result = @$this->api->postRequest( // @codingStandardsIgnoreLine
+ new SimpleRequest( 'query', [
+ 'meta' => 'tokens',
+ 'type' => $this->getNewTokenType( $type ),
+ 'continue' => '',
+ ] )
+ );
+ // If mw<1.25 (no new module)
+ $metaWarning = "Unrecognized value for parameter 'meta': tokens";
+ if ( isset( $result['warnings']['query']['*'] )
+ && false !== strpos( $result['warnings']['query']['*'], $metaWarning ) ) {
+ $this->usePre125TokensModule = true;
+ $this->logger->log( LogLevel::DEBUG, 'Falling back to pre 1.25 token system' );
+ $this->tokens[$type] = $this->reallyGetPre125Token( $type );
+ } else {
+ $this->tokens[$type] = array_pop( $result['query']['tokens'] );
+ }
+
+ return $this->tokens[$type];
+ }
+
+ /**
+ * Tries to guess a new token type from an old token type
+ *
+ * @param string $type
+ *
+ * @return string
+ */
+ private function getNewTokenType( $type ) {
+ switch ( $type ) {
+ case 'edit':
+ case 'delete':
+ case 'protect':
+ case 'move':
+ case 'block':
+ case 'unblock':
+ case 'email':
+ case 'import':
+ case 'options':
+ return 'csrf';
+ }
+ // Return the same type, don't know what to do with this..
+ return $type;
+ }
+
+ /**
+ * Tries to guess an old token type from a new token type
+ *
+ * @param $type
+ *
+ * @return string
+ */
+ private function getOldTokenType( $type ) {
+ switch ( $type ) {
+ // Guess that we want an edit token, this may not always work as we might be trying to
+ // use it for something else...
+ case 'csrf':
+ return 'edit';
+ }
+ return $type;
+ }
+
+ /**
+ * Clears all tokens stored by the api
+ *
+ * @since 0.2
+ */
+ public function clearTokens() {
+ $this->logger->log( LogLevel::DEBUG, 'Clearing session tokens', [ 'tokens' => $this->tokens ] );
+ $this->tokens = [];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php
new file mode 100644
index 00000000..8d3fbdf4
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/MultipartRequest.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Exception;
+
+/**
+ * A MultipartRequest is the same as a FluentRequest with additional support for setting request
+ * parameters (both normal parameters and headers) on multipart requests.
+ *
+ * @link http://docs.guzzlephp.org/en/stable/request-options.html#multipart
+ *
+ * @since 2.4.0
+ */
+class MultipartRequest extends FluentRequest {
+
+ /** @var mixed[] */
+ protected $multipartParams = [];
+
+ /**
+ * Check the structure of a multipart parameter array.
+ *
+ * @param mixed[] $params The multipart parameters to check.
+ *
+ * @throws Exception
+ */
+ protected function checkMultipartParams( $params ) {
+ foreach ( $params as $key => $val ) {
+ if ( !is_array( $val ) ) {
+ throw new Exception( "Parameter '$key' must be an array." );
+ }
+ if ( !in_array( $key, array_keys( $this->getParams() ) ) ) {
+ throw new Exception( "Parameter '$key' is not already set on this request." );
+ }
+ }
+ }
+
+ /**
+ * Set all multipart parameters, replacing all existing ones.
+ *
+ * Each key of the array passed in here must be the name of a parameter already set on this
+ * request object.
+ *
+ * @param mixed[] $params The multipart parameters to use.
+ * @return $this
+ */
+ public function setMultipartParams( $params ) {
+ $this->checkMultipartParams( $params );
+ $this->multipartParams = $params;
+ return $this;
+ }
+
+ /**
+ * Add extra multipart parameters.
+ *
+ * Each key of the array passed in here must be the name of a parameter already set on this
+ * request object.
+ *
+ * @param mixed[] $params The multipart parameters to add to any already present.
+ *
+ * @return $this
+ */
+ public function addMultipartParams( $params ) {
+ $this->checkMultipartParams( $params );
+ $this->multipartParams = array_merge( $this->multipartParams, $params );
+ return $this;
+ }
+
+ /**
+ * Get all multipart request parameters.
+ *
+ * @return mixed[]
+ */
+ public function getMultipartParams() {
+ return $this->multipartParams;
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Request.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Request.php
new file mode 100644
index 00000000..0daace2a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/Request.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Mediawiki\Api;
+
+/**
+ * @since 0.2
+ *
+ * @author Addshore
+ */
+interface Request {
+
+ /**
+ * @since 0.2
+ *
+ * @return array
+ */
+ public function getParams();
+
+ /**
+ * Associative array of headers to add to the request.
+ * Each key is the name of a header, and each value is a string or array of strings representing
+ * the header field values.
+ *
+ * @since 0.3
+ *
+ * @return array
+ */
+ public function getHeaders();
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/RsdException.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/RsdException.php
new file mode 100644
index 00000000..b304f574
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/RsdException.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Exception;
+
+/**
+ * An exception raised when an issue is encountered with Really Simple Discovery.
+ * @see https://en.wikipedia.org/wiki/Really_Simple_Discovery
+ */
+class RsdException extends Exception {
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php
new file mode 100644
index 00000000..1e33348d
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/SimpleRequest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use InvalidArgumentException;
+
+/**
+ * Please consider using a FluentRequest object
+ *
+ * @since 0.2
+ *
+ * @author Addshore
+ */
+class SimpleRequest implements Request {
+
+ /**
+ * @var string
+ */
+ private $action;
+
+ /**
+ * @var array
+ */
+ private $params;
+
+ /**
+ * @var array
+ */
+ private $headers;
+
+ /**
+ * @param string $action The API action.
+ * @param array $params The parameters for the action.
+ * @param array $headers Any extra HTTP headers to send.
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $action, array $params = [], array $headers = [] ) {
+ if ( !is_string( $action ) ) {
+ throw new InvalidArgumentException( '$action must be string' );
+ }
+ $this->action = $action;
+ $this->params = $params;
+ $this->headers = $headers;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getParams() {
+ return array_merge( [ 'action' => $this->action ], $this->params );
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getHeaders() {
+ return $this->headers;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/src/UsageException.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/UsageException.php
new file mode 100644
index 00000000..77148f1b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/src/UsageException.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Exception;
+
+/**
+ * Class representing a Mediawiki Api UsageException
+ *
+ * @since 0.1
+ *
+ * @author Addshore
+ */
+class UsageException extends Exception {
+
+ /**
+ * @var string
+ */
+ private $apiCode;
+
+ /**
+ * @var array
+ */
+ private $result;
+
+ /**
+ * @var string
+ */
+ private $rawMessage;
+
+ /**
+ * @since 0.1
+ *
+ * @param string $apiCode The API error code.
+ * @param string $message The API error message.
+ * @param array $result the result the exception was generated from
+ */
+ public function __construct( $apiCode = '', $message = '', $result = [] ) {
+ $this->apiCode = $apiCode;
+ $this->result = $result;
+ $this->rawMessage = $message;
+ $message = 'Code: ' . $apiCode . PHP_EOL .
+ 'Message: ' . $message . PHP_EOL .
+ 'Result: ' . json_encode( $result );
+ parent::__construct( $message, 0, null );
+ }
+
+ /**
+ * @since 0.1
+ *
+ * @return string
+ */
+ public function getApiCode() {
+ return $this->apiCode;
+ }
+
+ /**
+ * @since 0.3
+ *
+ * @return array
+ */
+ public function getApiResult() {
+ return $this->result;
+ }
+
+ /**
+ * @since 2.3.0
+ *
+ * @return string
+ */
+ public function getRawMessage() {
+ return $this->rawMessage;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/MediawikiApiTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/MediawikiApiTest.php
new file mode 100644
index 00000000..da6683e4
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/MediawikiApiTest.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Mediawiki\Api\Test\Integration;
+
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\SimpleRequest;
+
+/**
+ * @author Addshore
+ */
+class MediawikiApiTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @covers Mediawiki\Api\MediawikiApi::newFromPage
+ */
+ public function testNewFromPage() {
+ $api = MediawikiApi::newFromPage( TestEnvironment::newInstance()->getPageUrl() );
+ $this->assertInstanceOf( 'Mediawiki\Api\MediawikiApi', $api );
+ }
+
+ /**
+ * @covers Mediawiki\Api\MediawikiApi::newFromPage
+ * @expectedException Mediawiki\Api\RsdException
+ * @expectedExceptionMessageRegExp |Unable to find RSD URL in page.*|
+ */
+ public function testNewFromPageInvalidHtml() {
+ // This could be any URL that doesn't contain the RSD link, but the README URL
+ // is a test-accessible one that doesn't return 404.
+ $nonWikiPage = str_replace( 'api.php', 'README', TestEnvironment::newInstance()->getApiUrl() );
+ MediawikiApi::newFromPage( $nonWikiPage );
+ }
+
+ /**
+ * Duplicate element IDs break DOMDocument::loadHTML
+ * @see https://phabricator.wikimedia.org/T163527#3219833
+ * @covers Mediawiki\Api\MediawikiApi::newFromPage
+ */
+ public function testNewFromPageWithDuplicateId() {
+ $testPageName = __METHOD__;
+ $testEnv = TestEnvironment::newInstance();
+ $wikiPageUrl = str_replace( 'api.php', "index.php?title=$testPageName", $testEnv->getApiUrl() );
+
+ // Test with no duplicate IDs.
+ $testEnv->savePage( $testPageName, '<p id="unique-id"></p>' );
+ $api1 = MediawikiApi::newFromPage( $wikiPageUrl );
+ $this->assertInstanceOf( MediawikiApi::class, $api1 );
+
+ // Test with duplicate ID.
+ $wikiText = '<p id="duplicated-id"></p><div id="duplicated-id"></div>';
+ $testEnv->savePage( $testPageName, $wikiText );
+ $api2 = MediawikiApi::newFromPage( $wikiPageUrl );
+ $this->assertInstanceOf( MediawikiApi::class, $api2 );
+ }
+
+ /**
+ * @covers Mediawiki\Api\MediawikiApi::getRequest
+ * @covers Mediawiki\Api\MediawikiApi::getClientRequestOptions
+ * @covers Mediawiki\Api\MediawikiApi::decodeResponse
+ * @covers Mediawiki\Api\MediawikiApi::getClient
+ */
+ public function testQueryGetResponse() {
+ $api = TestEnvironment::newInstance()->getApi();
+ $response = $api->getRequest( new SimpleRequest( 'query' ) );
+ $this->assertInternalType( 'array', $response );
+ }
+
+ /**
+ * @covers Mediawiki\Api\MediawikiApi::getRequestAsync
+ * @covers Mediawiki\Api\MediawikiApi::getClientRequestOptions
+ * @covers Mediawiki\Api\MediawikiApi::decodeResponse
+ * @covers Mediawiki\Api\MediawikiApi::getClient
+ */
+ public function testQueryGetResponseAsync() {
+ $api = TestEnvironment::newInstance()->getApi();
+ $response = $api->getRequestAsync( new SimpleRequest( 'query' ) );
+ $this->assertInternalType( 'array', $response->wait() );
+ }
+
+ /**
+ * @covers Mediawiki\Api\MediawikiApi::postRequest
+ * @covers Mediawiki\Api\MediawikiApi::getClientRequestOptions
+ * @covers Mediawiki\Api\MediawikiApi::decodeResponse
+ * @covers Mediawiki\Api\MediawikiApi::getClient
+ */
+ public function testQueryPostResponse() {
+ $api = TestEnvironment::newInstance()->getApi();
+ $response = $api->postRequest( new SimpleRequest( 'query' ) );
+ $this->assertInternalType( 'array', $response );
+ }
+
+ /**
+ * @covers Mediawiki\Api\MediawikiApi::postRequestAsync
+ * @covers Mediawiki\Api\MediawikiApi::getClientRequestOptions
+ * @covers Mediawiki\Api\MediawikiApi::decodeResponse
+ * @covers Mediawiki\Api\MediawikiApi::getClient
+ */
+ public function testQueryPostResponseAsync() {
+ $api = TestEnvironment::newInstance()->getApi();
+ $response = $api->postRequestAsync( new SimpleRequest( 'query' ) );
+ $this->assertInternalType( 'array', $response->wait() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TestEnvironment.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TestEnvironment.php
new file mode 100644
index 00000000..cb781508
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TestEnvironment.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace Mediawiki\Api\Test\Integration;
+
+use Exception;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\SimpleRequest;
+
+/**
+ * @author Addshore
+ */
+class TestEnvironment {
+
+ /**
+ * Get a new TestEnvironment.
+ * This is identical to calling self::__construct() but is useful for fluent construction.
+ *
+ * @return TestEnvironment
+ */
+ public static function newInstance() {
+ return new self();
+ }
+
+ /** @var MediawikiApi */
+ private $api;
+
+ /** @var string */
+ private $apiUrl;
+
+ /** @var string */
+ private $pageUrl;
+
+ /**
+ * Set up the test environment by creating a new API object pointing to a
+ * MediaWiki installation on localhost (or elsewhere as specified by the
+ * ADDWIKI_MW_API environment variable).
+ *
+ * @throws Exception If the ADDWIKI_MW_API environment variable does not end in 'api.php'
+ */
+ public function __construct() {
+ $apiUrl = getenv( 'ADDWIKI_MW_API' );
+
+ if ( substr( $apiUrl, -7 ) !== 'api.php' ) {
+ $msg = "URL incorrect: $apiUrl"
+ ." (Set the ADDWIKI_MW_API environment variable correctly)";
+ throw new Exception( $msg );
+ }
+
+ $this->apiUrl = $apiUrl;
+ $this->pageUrl = str_replace( 'api.php', 'index.php?title=Special:SpecialPages', $apiUrl );
+ $this->api = MediawikiApi::newFromApiEndpoint( $this->apiUrl );
+ }
+
+ /**
+ * Get the url of the api to test against, based on the MEDIAWIKI_API_URL environment variable.
+ * @return string
+ */
+ public function getApiUrl() {
+ return $this->apiUrl;
+ }
+
+ /**
+ * Get the url of a page on the wiki to test against, based on the api url.
+ * @return string
+ */
+ public function getPageUrl() {
+ return $this->pageUrl;
+ }
+
+ /**
+ * Get the MediawikiApi to test against
+ * @return MediawikiApi
+ */
+ public function getApi() {
+ return $this->api;
+ }
+
+ /**
+ * Save a wiki page.
+ * @param string $title The title of the page.
+ * @param string $content The complete page text to save.
+ */
+ public function savePage( $title, $content ) {
+ $params = [
+ 'title' => $title,
+ 'text' => $content,
+ 'md5' => md5( $content ),
+ 'token' => $this->api->getToken(),
+ ];
+ $this->api->postRequest( new SimpleRequest( 'edit', $params ) );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TokenHandlingTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TokenHandlingTest.php
new file mode 100644
index 00000000..68ec52be
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Integration/TokenHandlingTest.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Mediawiki\Api\Test\Integration;
+
+/**
+ * @author Addshore
+ */
+class TokenHandlingTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideTokenTypes
+ *
+ * @covers Mediawiki\Api\MediawikiApi::getToken
+ * @covers Mediawiki\Api\MediawikiSession::getToken
+ */
+ public function testGetAnonUserToken() {
+ $api = TestEnvironment::newInstance()->getApi();
+ $this->assertEquals( '+\\', $api->getToken() );
+ }
+
+ public function provideTokenTypes() {
+ return [
+ [ 'csrf' ],
+ [ 'edit' ],
+ ];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/ApiUserTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/ApiUserTest.php
new file mode 100644
index 00000000..7e0da7ca
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/ApiUserTest.php
@@ -0,0 +1,71 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Mediawiki\Api\ApiUser;
+
+/**
+ * @author Addshore
+ *
+ * @covers Mediawiki\Api\ApiUser
+ */
+class ApiUserTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $user, $pass, $domain = null ) {
+ $apiUser = new ApiUser( $user, $pass, $domain );
+ $this->assertSame( $user, $apiUser->getUsername() );
+ $this->assertSame( $pass, $apiUser->getPassword() );
+ $this->assertSame( $domain, $apiUser->getDomain() );
+ }
+
+ public function provideValidConstruction() {
+ return [
+ [ 'user', 'pass' ],
+ [ 'user', 'pass', 'domain' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction( $user, $pass, $domain = null ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new ApiUser( $user, $pass, $domain );
+ }
+
+ public function provideInvalidConstruction() {
+ return [
+ [ 'user', '' ],
+ [ '', 'pass' ],
+ [ '', '' ],
+ [ 'user', [] ],
+ [ 'user', 455667 ],
+ [ 34567, 'pass' ],
+ [ [], 'pass' ],
+ [ 'user', 'pass', [] ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideTestEquals
+ */
+ public function testEquals( ApiUser $user1, ApiUser $user2, $shouldEqual ) {
+ $this->assertSame( $shouldEqual, $user1->equals( $user2 ) );
+ $this->assertSame( $shouldEqual, $user2->equals( $user1 ) );
+ }
+
+ public function provideTestEquals() {
+ return [
+ [ new ApiUser( 'usera', 'passa' ), new ApiUser( 'usera', 'passa' ), true ],
+ [ new ApiUser( 'usera', 'passa', 'domain' ), new ApiUser( 'usera', 'passa', 'domain' ), true ],
+ [ new ApiUser( 'DIFF', 'passa' ), new ApiUser( 'usera', 'passa' ), false ],
+ [ new ApiUser( 'usera', 'DIFF' ), new ApiUser( 'usera', 'passa' ), false ],
+ [ new ApiUser( 'usera', 'passa' ), new ApiUser( 'DIFF', 'passa' ), false ],
+ [ new ApiUser( 'usera', 'passa' ), new ApiUser( 'usera', 'DIFF' ), false ],
+ ];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/FluentRequestTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/FluentRequestTest.php
new file mode 100644
index 00000000..93af921e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/FluentRequestTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Mediawiki\Api\FluentRequest;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ *
+ * @covers Mediawiki\Api\FluentRequest
+ */
+class FluentRequestTest extends PHPUnit_Framework_TestCase {
+
+ public function testFactory() {
+ $this->assertInstanceOf( 'Mediawiki\Api\FluentRequest', FluentRequest::factory() );
+ }
+
+ public function testConstructionDefaults() {
+ $request = new FluentRequest();
+
+ $this->assertEquals( [], $request->getParams() );
+ $this->assertEquals( [], $request->getHeaders() );
+ }
+
+ public function testSetParams() {
+ $request = new FluentRequest();
+
+ $params = [ 'foo', 'bar' ];
+ $request->setParams( $params );
+
+ $this->assertEquals( $params, $request->getParams() );
+ }
+
+ public function testSetParam() {
+ $request = new FluentRequest();
+
+ $request->setParam( 'paramName', 'fooValue' );
+
+ $this->assertEquals( [ 'paramName' => 'fooValue' ], $request->getParams() );
+ }
+
+ public function testAddParams() {
+ $request = new FluentRequest();
+
+ $params = [ 'a' => 'foo', 'b' => 'bar' ];
+ $request->addParams( $params );
+
+ $this->assertEquals( $params, $request->getParams() );
+ }
+
+ public function testSetHeaders() {
+ $request = new FluentRequest();
+
+ $params = [ 'foo', 'bar' ];
+ $request->setHeaders( $params );
+
+ $this->assertEquals( $params, $request->getHeaders() );
+ }
+
+ public function testSetAction() {
+ $request = new FluentRequest();
+
+ $request->setAction( 'fooAction' );
+
+ $this->assertEquals( [ 'action' => 'fooAction' ], $request->getParams() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/ClientFactoryTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/ClientFactoryTest.php
new file mode 100644
index 00000000..d84d0333
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/ClientFactoryTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit\Guzzle;
+
+use GuzzleHttp\HandlerStack;
+use Mediawiki\Api\Guzzle\ClientFactory;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * @author Christian Schmidt
+ *
+ * @covers Mediawiki\Api\Guzzle\ClientFactory
+ */
+class ClientFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testNoConfig() {
+ $clientFactory = new ClientFactory();
+
+ $client = $clientFactory->getClient();
+
+ $this->assertSame( $client, $clientFactory->getClient() );
+
+ $config = $client->getConfig();
+ $this->assertEquals( $config['headers']['User-Agent'], 'Addwiki - mediawiki-api-base' );
+
+ $this->assertFalse( empty( $config['cookies'] ) );
+ }
+
+ public function testUserAgent() {
+ $clientFactory = new ClientFactory( [ 'user-agent' => 'Foobar' ] );
+
+ $client = $clientFactory->getClient();
+
+ $this->assertNull( $client->getConfig( 'user-agent' ) );
+
+ $config = $client->getConfig();
+ $this->assertEquals( $config['headers']['User-Agent'], 'Foobar' );
+ }
+
+ public function testHeaders() {
+ $clientFactory = new ClientFactory( [
+ 'headers' => [
+ 'User-Agent' => 'Foobar',
+ 'X-Foo' => 'Bar',
+ ]
+ ] );
+
+ $client = $clientFactory->getClient();
+
+ $headers = $client->getConfig( 'headers' );
+ $this->assertCount( 2, $headers );
+ $this->assertEquals( $headers['User-Agent'], 'Foobar' );
+ $this->assertEquals( $headers['X-Foo'], 'Bar' );
+ }
+
+ public function testHandler() {
+ $handler = HandlerStack::create();
+
+ $clientFactory = new ClientFactory( [ 'handler' => $handler ] );
+
+ $client = $clientFactory->getClient();
+
+ $this->assertSame( $handler, $client->getConfig( 'handler' ) );
+ }
+
+ public function testMiddleware() {
+ $invoked = false;
+ $middleware = function () use ( &$invoked ) {
+ return function () use ( &$invoked ) {
+ $invoked = true;
+ };
+ };
+
+ $clientFactory = new ClientFactory( [ 'middleware' => [ $middleware ] ] );
+
+ $client = $clientFactory->getClient();
+
+ $this->assertNull( $client->getConfig( 'middleware' ) );
+
+ $request = $this->getMockBuilder( RequestInterface::class )->getMock();
+
+ $handler = $client->getConfig( 'handler' );
+ $handler->remove( 'http_errors' );
+ $handler->remove( 'allow_redirects' );
+ $handler->remove( 'cookies' );
+ $handler->remove( 'prepare_body' );
+ $handler( $request, [] );
+
+ $this->assertTrue( $invoked );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/MiddlewareFactoryTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/MiddlewareFactoryTest.php
new file mode 100644
index 00000000..1cf7270a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/Guzzle/MiddlewareFactoryTest.php
@@ -0,0 +1,214 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit\Guzzle;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Handler\MockHandler;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Psr7\Request;
+use GuzzleHttp\Psr7\Response;
+use Mediawiki\Api\Guzzle\MiddlewareFactory;
+
+/**
+ * @author Addshore
+ *
+ * @todo test interaction with logger
+ *
+ * @covers Mediawiki\Api\Guzzle\MiddlewareFactory
+ */
+class MiddlewareFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function testRetriesConnectException() {
+ $queue = [
+ new ConnectException( 'Error 1', new Request( 'GET', 'test' ) ),
+ new Response( 200, [ 'X-Foo' => 'Bar' ] ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals( [ 1000 ], $delays );
+ }
+
+ public function testRetries500Errors() {
+ $queue = [
+ new Response( 500 ),
+ new Response( 200 ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals( [ 1000 ], $delays );
+ }
+
+ public function testRetriesSomeMediawikiApiErrorHeaders() {
+ $queue = [
+ new Response( 200, [ 'mediawiki-api-error' => 'ratelimited' ] ),
+ new Response( 200, [ 'mediawiki-api-error' => 'maxlag' ] ),
+ new Response( 200, [ 'mediawiki-api-error' => 'readonly' ] ),
+ new Response( 200, [ 'mediawiki-api-error' => 'internal_api_error_DBQueryError' ] ),
+ new Response( 200, [ 'mediawiki-api-error' => 'DoNotRetryThisHeader' ] ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals(
+ [ 'DoNotRetryThisHeader' ],
+ $response->getHeader( 'mediawiki-api-error' )
+ );
+ $this->assertEquals( [ 1000, 2000, 3000, 4000 ], $delays );
+ }
+
+ public function testRetryAntiAbuseMeasure() {
+ $antiAbusejson = json_encode(
+ [
+ 'error' => [
+ 'info' => 'anti-abuse measure'
+ ]
+ ]
+ );
+
+ $queue = [
+ new Response( 200, [ 'mediawiki-api-error' => 'failed-save' ], $antiAbusejson ),
+ new Response( 200, [ 'mediawiki-api-error' => 'DoNotRetryThisHeader' ] ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals( 'DoNotRetryThisHeader', $response->getHeaderLine( 'mediawiki-api-error' ) );
+ }
+
+ public function testRetryLimit() {
+ $queue = [
+ new ConnectException( 'Error 1', new Request( 'GET', 'test' ) ),
+ new ConnectException( 'Error 2', new Request( 'GET', 'test' ) ),
+ new ConnectException( 'Error 3', new Request( 'GET', 'test' ) ),
+ new ConnectException( 'Error 4', new Request( 'GET', 'test' ) ),
+ new ConnectException( 'Error 5', new Request( 'GET', 'test' ) ),
+ new ConnectException( 'Error 6', new Request( 'GET', 'test' ) ),
+ new Response( 200 ),
+ ];
+
+ $client = $this->getClient( $queue );
+
+ $this->setExpectedException(
+ 'GuzzleHttp\Exception\ConnectException',
+ 'Error 6'
+ );
+
+ $client->request( 'GET', '/' );
+ }
+
+ public function testConnectExceptionRetryDelay() {
+ $queue = [
+ new ConnectException( '+1 second delay', new Request( 'GET', 'test' ) ),
+ new ConnectException( '+2 second delay', new Request( 'GET', 'test' ) ),
+ new Response( 200 ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals( [ 1000, 2000 ], $delays );
+ }
+
+ public function testServerErrorRetryDelay() {
+ $queue = [
+ new Response( 500 ),
+ new Response( 503 ),
+ new Response( 200 ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals( [ 1000, 2000 ], $delays );
+ }
+
+ public function testRelativeRetryDelayHeaderRetryDelay() {
+ $queue = [
+ new Response( 200, [ 'mediawiki-api-error' => 'maxlag', 'retry-after' => 10 ] ),
+ new Response( 200 ),
+ ];
+
+ $this->getClient( $queue, $delays )->request( 'GET', '/' );
+
+ $this->assertEquals( [ 10000 ], $delays );
+ }
+
+ public function testAbsoluteRetryDelayHeaderRetryDelay() {
+ $queue = [
+ new Response(
+ 200,
+ [
+ 'mediawiki-api-error' => 'maxlag',
+ 'retry-after' => gmdate( DATE_RFC1123, time() + 600 ),
+ ]
+ ),
+ new Response( 200 ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertCount( 1, $delays );
+ // Allow 5 second delay while running this test.
+ $this->assertGreaterThan( 600000 - 5000, $delays[0] );
+ }
+
+ public function testPastRetryDelayHeaderRetryDelay() {
+ $queue = [
+ new Response(
+ 200,
+ [
+ 'mediawiki-api-error' => 'maxlag',
+ 'retry-after' => 'Fri, 31 Dec 1999 23:59:59 GMT',
+ ]
+ ),
+ new Response( 200 ),
+ ];
+
+ $client = $this->getClient( $queue, $delays );
+ $response = $client->request( 'GET', '/' );
+
+ $this->assertEquals( 200, $response->getStatusCode() );
+ $this->assertEquals( [ 1000 ], $delays );
+ }
+
+ private function getClient( array $queue, &$delays = null ) {
+ $mock = new MockHandler( $queue );
+
+ $handler = HandlerStack::create( $mock );
+
+ $middlewareFactory = new MiddlewareFactory();
+ $handler->push( $middlewareFactory->retry() );
+
+ $delayMocker = $this->getDelayMocker( $delays );
+ $handler->push( $delayMocker );
+
+ return new Client( [ 'handler' => $handler ] );
+ }
+
+ private function getDelayMocker( &$delays ) {
+ return function ( callable $handler ) use ( &$delays ) {
+ return function ( $request, array $options ) use ( $handler, &$delays ) {
+ if ( isset( $options['delay'] ) ) {
+ $delays[] = $options['delay'];
+ unset( $options['delay'] );
+ }
+ return $handler( $request, $options );
+ };
+ };
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiApiTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiApiTest.php
new file mode 100644
index 00000000..a55f6739
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiApiTest.php
@@ -0,0 +1,296 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Mediawiki\Api\ApiUser;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\Api\UsageException;
+use PHPUnit_Framework_TestCase;
+use stdClass;
+
+/**
+ * @author Addshore
+ *
+ * @covers Mediawiki\Api\MediawikiApi
+ */
+class MediawikiApiTest extends PHPUnit_Framework_TestCase {
+
+ public function provideValidConstruction() {
+ return [
+ [ 'localhost' ],
+ [ 'http://en.wikipedia.org/w/api.php' ],
+ [ '127.0.0.1/foo/bar/wwwwwwwww/api.php' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $apiLocation ) {
+ new MediawikiApi( $apiLocation );
+ $this->assertTrue( true );
+ }
+
+ public function provideInvalidConstruction() {
+ return [
+ [ null ],
+ [ 12345678 ],
+ [ [] ],
+ [ new stdClass() ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction( $apiLocation ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new MediawikiApi( $apiLocation );
+ }
+
+ private function getMockClient() {
+ return $this->getMock( 'GuzzleHttp\ClientInterface' );
+ }
+
+ private function getMockResponse( $responseValue ) {
+ $mock = $this->getMock( 'Psr\Http\Message\ResponseInterface' );
+ $mock->expects( $this->any() )
+ ->method( 'getBody' )
+ ->will( $this->returnValue( json_encode( $responseValue ) ) );
+ return $mock;
+ }
+
+ private function getExpectedRequestOpts( $params, $paramsLocation ) {
+ return [
+ $paramsLocation => array_merge( $params, [ 'format' => 'json' ] ),
+ 'headers' => [ 'User-Agent' => 'addwiki-mediawiki-client' ],
+ ];
+ }
+
+ public function testGetRequestThrowsUsageExceptionOnError() {
+ $client = $this->getMockClient();
+ $client->expects( $this->once() )
+ ->method( 'request' )
+ ->will( $this->returnValue(
+ $this->getMockResponse( [ 'error' => [
+ 'code' => 'imacode',
+ 'info' => 'imamsg',
+ ] ] )
+ ) );
+ $api = new MediawikiApi( '', $client );
+
+ try{
+ $api->getRequest( new SimpleRequest( 'foo' ) );
+ $this->fail( 'No Usage Exception Thrown' );
+ }
+ catch ( UsageException $e ) {
+ $this->assertEquals( 'imacode', $e->getApiCode() );
+ $this->assertEquals( 'imamsg', $e->getRawMessage() );
+ }
+ }
+
+ public function testPostRequestThrowsUsageExceptionOnError() {
+ $client = $this->getMockClient();
+ $client->expects( $this->once() )
+ ->method( 'request' )
+ ->will( $this->returnValue(
+ $this->getMockResponse( [ 'error' => [
+ 'code' => 'imacode',
+ 'info' => 'imamsg',
+ ] ] )
+ ) );
+ $api = new MediawikiApi( '', $client );
+
+ try{
+ $api->postRequest( new SimpleRequest( 'foo' ) );
+ $this->fail( 'No Usage Exception Thrown' );
+ }
+ catch ( UsageException $e ) {
+ $this->assertSame( 'imacode', $e->getApiCode() );
+ $this->assertSame( 'imamsg', $e->getRawMessage() );
+ }
+ }
+
+ /**
+ * @dataProvider provideActionsParamsResults
+ */
+ public function testGetActionReturnsResult( $expectedResult, $action, $params = [] ) {
+ $client = $this->getMockClient();
+ $params = array_merge( [ 'action' => $action ], $params );
+ $client->expects( $this->once() )
+ ->method( 'request' )
+ ->with( 'GET', null, $this->getExpectedRequestOpts( $params, 'query' ) )
+ ->will( $this->returnValue( $this->getMockResponse( $expectedResult ) ) );
+ $api = new MediawikiApi( '', $client );
+
+ $result = $api->getRequest( new SimpleRequest( $action, $params ) );
+
+ $this->assertEquals( $expectedResult, $result );
+ }
+
+ /**
+ * @dataProvider provideActionsParamsResults
+ */
+ public function testPostActionReturnsResult( $expectedResult, $action, $params = [] ) {
+ $client = $this->getMockClient();
+ $params = array_merge( [ 'action' => $action ], $params );
+ $client->expects( $this->once() )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( $params, 'form_params' ) )
+ ->will( $this->returnValue( $this->getMockResponse( $expectedResult ) ) );
+ $api = new MediawikiApi( '', $client );
+
+ $result = $api->postRequest( new SimpleRequest( $action, $params ) );
+
+ $this->assertEquals( $expectedResult, $result );
+ }
+
+ private function getNullFilePointer() {
+ if ( !file_exists( '/dev/null' ) ) {
+ // windows
+ return fopen( 'NUL', 'r' );
+ }
+ return fopen( '/dev/null', 'r' );
+ }
+
+ public function testPostActionWithFileReturnsResult() {
+ $dummyFile = $this->getNullFilePointer();
+ $params = [
+ 'filename' => 'foo.jpg',
+ 'file' => $dummyFile,
+ ];
+ $client = $this->getMockClient();
+ $client->expects( $this->once() )->method( 'request' )->with(
+ 'POST',
+ null,
+ [
+ 'multipart' => [
+ [ 'name' => 'action', 'contents' => 'upload' ],
+ [ 'name' => 'filename', 'contents' => 'foo.jpg' ],
+ [ 'name' => 'file', 'contents' => $dummyFile ],
+ [ 'name' => 'format', 'contents' => 'json' ],
+ ],
+ 'headers' => [ 'User-Agent' => 'addwiki-mediawiki-client' ],
+ ]
+ )->will( $this->returnValue( $this->getMockResponse( [ 'success ' => 1 ] ) ) );
+ $api = new MediawikiApi( '', $client );
+
+ $result = $api->postRequest( new SimpleRequest( 'upload', $params ) );
+
+ $this->assertEquals( [ 'success ' => 1 ], $result );
+ }
+
+ public function provideActionsParamsResults() {
+ return [
+ [ [ 'key' => 'value' ], 'logout' ],
+ [ [ 'key' => 'value' ], 'logout', [ 'param1' => 'v1' ] ],
+ [ [ 'key' => 'value', 'key2' => 1212, [] ], 'logout' ],
+ ];
+ }
+
+ public function testGoodLoginSequence() {
+ $client = $this->getMockClient();
+ $user = new ApiUser( 'U1', 'P1' );
+ $eq1 = [
+ 'action' => 'login',
+ 'lgname' => 'U1',
+ 'lgpassword' => 'P1',
+ ];
+ $client->expects( $this->at( 0 ) )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( $eq1, 'form_params' ) )
+ ->will( $this->returnValue( $this->getMockResponse( [ 'login' => [
+ 'result' => 'NeedToken',
+ 'token' => 'IamLoginTK',
+ ] ] ) ) );
+ $params = array_merge( $eq1, [ 'lgtoken' => 'IamLoginTK' ] );
+ $response = $this->getMockResponse( [ 'login' => [ 'result' => 'Success' ] ] );
+ $client->expects( $this->at( 1 ) )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( $params, 'form_params' ) )
+ ->will( $this->returnValue( $response ) );
+ $api = new MediawikiApi( '', $client );
+
+ $this->assertTrue( $api->login( $user ) );
+ $this->assertSame( 'U1', $api->isLoggedin() );
+ }
+
+ public function testBadLoginSequence() {
+ $client = $this->getMockClient();
+ $user = new ApiUser( 'U1', 'P1' );
+ $eq1 = [
+ 'action' => 'login',
+ 'lgname' => 'U1',
+ 'lgpassword' => 'P1',
+ ];
+ $client->expects( $this->at( 0 ) )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( $eq1, 'form_params' ) )
+ ->will( $this->returnValue( $this->getMockResponse( [ 'login' => [
+ 'result' => 'NeedToken',
+ 'token' => 'IamLoginTK',
+ ] ] ) ) );
+ $params = array_merge( $eq1, [ 'lgtoken' => 'IamLoginTK' ] );
+ $response = $this->getMockResponse( [ 'login' => [ 'result' => 'BADTOKENorsmthin' ] ] );
+ $client->expects( $this->at( 1 ) )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( $params, 'form_params' ) )
+ ->will( $this->returnValue( $response ) );
+ $api = new MediawikiApi( '', $client );
+
+ $this->setExpectedException( 'Mediawiki\Api\UsageException' );
+ $api->login( $user );
+ }
+
+ public function testLogout() {
+ $client = $this->getMockClient();
+ $client->expects( $this->at( 0 ) )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( [ 'action' => 'logout' ], 'form_params' ) )
+ ->will( $this->returnValue( $this->getMockResponse( [] ) ) );
+ $api = new MediawikiApi( '', $client );
+
+ $this->assertTrue( $api->logout() );
+ }
+
+ public function testLogoutOnFailure() {
+ $client = $this->getMockClient();
+ $client->expects( $this->at( 0 ) )
+ ->method( 'request' )
+ ->with( 'POST', null, $this->getExpectedRequestOpts( [ 'action' => 'logout' ], 'form_params' ) )
+ ->will( $this->returnValue( $this->getMockResponse( null ) ) );
+ $api = new MediawikiApi( '', $client );
+
+ $this->assertFalse( $api->logout() );
+ }
+
+ /**
+ * @dataProvider provideVersions
+ */
+ public function testGetVersion( $apiValue, $expectedVersion ) {
+ $client = $this->getMockClient();
+ $params = [ 'action' => 'query', 'meta' => 'siteinfo', 'continue' => '' ];
+ $client->expects( $this->exactly( 1 ) )
+ ->method( 'request' )
+ ->with( 'GET', null, $this->getExpectedRequestOpts( $params, 'query' ) )
+ ->will( $this->returnValue( $this->getMockResponse( [
+ 'query' => [
+ 'general' => [
+ 'generator' => $apiValue,
+ ],
+ ],
+ ] ) ) );
+ $api = new MediawikiApi( '', $client );
+ $this->assertEquals( $expectedVersion, $api->getVersion() );
+ }
+
+ public function provideVersions() {
+ return [
+ [ 'MediaWiki 1.25wmf13', '1.25' ],
+ [ 'MediaWiki 1.24.1', '1.24.1' ],
+ [ 'MediaWiki 1.19', '1.19' ],
+ [ 'MediaWiki 1.0.0', '1.0.0' ],
+ ];
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiSessionTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiSessionTest.php
new file mode 100644
index 00000000..667a526f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MediawikiSessionTest.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\MediawikiSession;
+use PHPUnit_Framework_MockObject_MockObject;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ *
+ * @covers Mediawiki\Api\MediawikiSession
+ */
+class MediawikiSessionTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @return PHPUnit_Framework_MockObject_MockObject|MediawikiApi
+ */
+ private function getMockApi() {
+ return $this->getMockBuilder( '\Mediawiki\Api\MediawikiApi' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function testConstruction() {
+ $session = new MediawikiSession( $this->getMockApi() );
+ $this->assertInstanceOf( '\Mediawiki\Api\MediawikiSession', $session );
+ }
+
+ /**
+ * @dataProvider provideTokenTypes
+ */
+ public function testGetToken( $tokenType ) {
+ $mockApi = $this->getMockApi();
+ $mockApi->expects( $this->exactly( 2 ) )
+ ->method( 'postRequest' )
+ ->with( $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' ) )
+ ->will( $this->returnValue( [
+ 'query' => [
+ 'tokens' => [
+ $tokenType => 'TKN-' . $tokenType,
+ ]
+ ]
+ ] ) );
+
+ $session = new MediawikiSession( $mockApi );
+
+ // Although we make 2 calls to the method we assert the tokens method about is only called once
+ $this->assertEquals( 'TKN-' . $tokenType, $session->getToken() );
+ $this->assertEquals( 'TKN-' . $tokenType, $session->getToken() );
+ // Then clearing the tokens and calling again should make a second call!
+ $session->clearTokens();
+ $this->assertEquals( 'TKN-' . $tokenType, $session->getToken() );
+ }
+
+ /**
+ * @dataProvider provideTokenTypes
+ */
+ public function testGetTokenPre125( $tokenType ) {
+ $mockApi = $this->getMockApi();
+ $mockApi->expects( $this->at( 0 ) )
+ ->method( 'postRequest' )
+ ->with( $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' ) )
+ ->will( $this->returnValue( [
+ 'warnings' => [
+ 'query' => [
+ '*' => "Unrecognized value for parameter 'meta': tokens",
+ ]
+ ]
+ ] ) );
+ $mockApi->expects( $this->at( 1 ) )
+ ->method( 'postRequest' )
+ ->with( $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' ) )
+ ->will( $this->returnValue( [
+ 'tokens' => [
+ $tokenType => 'TKN-' . $tokenType,
+ ]
+ ] ) );
+
+ $session = new MediawikiSession( $mockApi );
+
+ // Although we make 2 calls to the method we assert the tokens method about is only called once
+ $this->assertSame( 'TKN-' . $tokenType, $session->getToken() );
+ $this->assertSame( 'TKN-' . $tokenType, $session->getToken() );
+ }
+
+ public function provideTokenTypes() {
+ return [
+ [ 'csrf' ],
+ [ 'edit' ],
+ ];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MultipartRequestTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MultipartRequestTest.php
new file mode 100644
index 00000000..993c29e8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/MultipartRequestTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Exception;
+use Mediawiki\Api\MultipartRequest;
+use PHPUnit_Framework_TestCase;
+
+class MultipartRequestTest extends PHPUnit_Framework_TestCase {
+
+ public function testBasics() {
+ $request = new MultipartRequest();
+ $this->assertEquals( [], $request->getMultipartParams() );
+
+ // One parameter.
+ $request->setParam( 'testparam', 'value' );
+ $request->addMultipartParams( [ 'testparam' => [ 'lorem' => 'ipsum' ] ] );
+ $this->assertEquals(
+ [ 'testparam' => [ 'lorem' => 'ipsum' ] ],
+ $request->getMultipartParams()
+ );
+
+ // Another parameter.
+ $request->setParam( 'testparam2', 'value' );
+ $request->addMultipartParams( [ 'testparam2' => [ 'lorem2' => 'ipsum2' ] ] );
+ $this->assertEquals(
+ [
+ 'testparam' => [ 'lorem' => 'ipsum' ],
+ 'testparam2' => [ 'lorem2' => 'ipsum2' ],
+ ],
+ $request->getMultipartParams()
+ );
+ }
+
+ /**
+ * You are not allowed to set multipart parameters on a parameter that doesn't exist.
+ * @expectedException Exception
+ * @expectedExceptionMessage Parameter 'testparam' is not already set on this request.
+ */
+ public function testParamNotYetSet() {
+ $request = new MultipartRequest();
+ $request->addMultipartParams( [ 'testparam' => [] ] );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/SimpleRequestTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/SimpleRequestTest.php
new file mode 100644
index 00000000..df979992
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/SimpleRequestTest.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Mediawiki\Api\SimpleRequest;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ *
+ * @covers Mediawiki\Api\SimpleRequest
+ */
+class SimpleRequestTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $action, $params, $expected, $headers = [] ) {
+ $request = new SimpleRequest( $action, $params, $headers );
+ $this->assertEquals( $expected, $request->getParams() );
+ $this->assertEquals( $headers, $request->getHeaders() );
+ }
+
+ public function provideValidConstruction() {
+ return [
+ [ 'action', [], [ 'action' => 'action' ] ],
+ [ '1123', [], [ 'action' => '1123' ] ],
+ [ 'a', [ 'b' => 'c' ], [ 'action' => 'a', 'b' => 'c' ] ],
+ [ 'a', [ 'b' => 'c', 'd' => 'e' ], [ 'action' => 'a', 'b' => 'c', 'd' => 'e' ] ],
+ [ 'a', [ 'b' => 'c|d|e|f' ], [ 'action' => 'a', 'b' => 'c|d|e|f' ] ],
+ [ 'foo', [], [ 'action' => 'foo' ] ,[ 'foo' => 'bar' ] ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction( $action, $params ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new SimpleRequest( $action, $params );
+ }
+
+ public function provideInvalidConstruction() {
+ return [
+ [ [], [] ],
+ ];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/UsageExceptionTest.php b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/UsageExceptionTest.php
new file mode 100644
index 00000000..2b7d6072
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api-base/tests/Unit/UsageExceptionTest.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Mediawiki\Api\Test\Unit;
+
+use Mediawiki\Api\UsageException;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ *
+ * @covers Mediawiki\Api\UsageException
+ */
+class UsageExceptionTest extends PHPUnit_Framework_TestCase {
+
+ public function testUsageExceptionWithNoParams() {
+ $e = new UsageException();
+ $this->assertSame(
+ 'Code: ' . PHP_EOL .
+ 'Message: ' . PHP_EOL .
+ 'Result: []',
+ $e->getMessage()
+ );
+ $this->assertSame( '', $e->getApiCode() );
+ $this->assertEquals( [], $e->getApiResult() );
+ }
+
+ public function testUsageExceptionWithParams() {
+ $e = new UsageException( 'imacode', 'imamsg', [ 'foo' => 'bar' ] );
+ $this->assertSame( 'imacode', $e->getApiCode() );
+ $this->assertSame(
+ 'Code: imacode' . PHP_EOL .
+ 'Message: imamsg' . PHP_EOL .
+ 'Result: {"foo":"bar"}',
+ $e->getMessage()
+ );
+ $this->assertEquals( [ 'foo' => 'bar' ], $e->getApiResult() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/.gitignore b/bin/wiki/vendor/addwiki/mediawiki-api/.gitignore
new file mode 100644
index 00000000..2bd8a05e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/.gitignore
@@ -0,0 +1,9 @@
+.idea
+vendor
+composer.lock
+test.php
+phpunit.xml
+nbproject
+docs/_build
+log
+
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml b/bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml
new file mode 100644
index 00000000..ffc976e3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/.scrutinizer.yml
@@ -0,0 +1,13 @@
+inherit: true
+
+tools:
+ php_code_sniffer: true
+ php_cpd: true
+ php_cs_fixer: true
+ php_loc: true
+ php_mess_detector: true
+ php_pdepend: true
+ php_analyzer: true
+ sensiolabs_security_checker: true
+ external_code_coverage:
+ timeout: 300 \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml b/bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml
new file mode 100644
index 00000000..26793151
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/.travis.yml
@@ -0,0 +1,52 @@
+language: php
+
+php:
+ - 5.5
+ - 5.6
+ - 7.0
+
+env:
+ matrix:
+ - TEST_SUITE=unit
+ # All the currently-supported versions from https://www.mediawiki.org/wiki/Version_lifecycle
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.26.4
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.27.3
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.28.2
+ - TEST_SUITE=integration MEDIAWIKI_VERSION=1.29.1
+ global:
+ - MEDIAWIKI_API_URL='http://127.0.0.1:8081/api.php'
+
+matrix:
+ include:
+ - php: hhvm
+ env: TEST_SUITE=unit
+ before_install:
+ install:
+ - composer install
+ - php: 7.1
+ env: TEST_SUITE=unit
+ before_install:
+ install:
+ - composer install
+
+before_install:
+ - bin/install-mediawiki.sh
+
+install:
+ - php -S 127.0.0.1:8081 -t build/mediawiki >/dev/null 2>&1 &
+ - composer install
+
+script:
+ - $TRAVIS_BUILD_DIR/vendor/bin/phpunit --coverage-clover=$TRAVIS_BUILD_DIR/coverage.clover $TRAVIS_BUILD_DIR/tests/$TEST_SUITE
+ - $TRAVIS_BUILD_DIR/vendor/bin/phpcs
+
+after_script:
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover $TRAVIS_BUILD_DIR/coverage.clover
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net##add"
+ on_success: change
+ on_failure: always
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md b/bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md
new file mode 100644
index 00000000..0671f06a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/LICENSE.md
@@ -0,0 +1,264 @@
+The GNU General Public License, Version 2, June 1991 (GPLv2)
+============================================================
+
+> Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+
+Preamble
+--------
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you can
+do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's free
+use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+
+Terms And Conditions For Copying, Distribution And Modification
+---------------------------------------------------------------
+
+**0.** This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program or
+work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included without
+limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+**1.** You may copy and distribute verbatim copies of the Program's source code
+as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the Program
+a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+**2.** You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+* **a)** You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+* **b)** You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof, to
+ be licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+* **c)** If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the entire whole,
+and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+**3.** You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+* **a)** Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above on
+ a medium customarily used for software interchange; or,
+
+* **b)** Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+* **c)** Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only for
+ noncommercial distribution and only if you received the program in object
+ code or executable form with such an offer, in accord with Subsection b
+ above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code distributed
+need not include anything that is normally distributed (in either source or
+binary form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component itself
+accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source code
+from the same place counts as distribution of the source code, even though third
+parties are not compelled to copy the source along with the object code.
+
+**4.** You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+**5.** You are not required to accept this License, since you have not signed
+it. However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you do
+not accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+**6.** Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+**7.** If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution of
+the Program by all those who receive copies directly or indirectly through you,
+then the only way you could satisfy both it and this License would be to refrain
+entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and the
+section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+**8.** If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+**9.** The Free Software Foundation may publish revised and/or new versions of
+the General Public License from time to time. Such new versions will be similar
+in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that version
+or of any later version published by the Free Software Foundation. If the
+Program does not specify a version number of this License, you may choose any
+version ever published by the Free Software Foundation.
+
+**10.** If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+
+No Warranty
+-----------
+
+**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/README.md b/bin/wiki/vendor/addwiki/mediawiki-api/README.md
new file mode 100644
index 00000000..325958fc
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/README.md
@@ -0,0 +1,74 @@
+mediawiki-api
+==================
+[![Build Status](https://travis-ci.org/addwiki/mediawiki-api.png?branch=master)](https://travis-ci.org/addwiki/mediawiki-api)
+[![Code Coverage](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/badges/coverage.png?s=5bce1c1f0939d278ac715c7846b679a61401b1de)](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/badges/quality-score.png?s=4182ebaf18fb0b22af9bc3e7941fd4e3524c932e)](https://scrutinizer-ci.com/g/addwiki/mediawiki-api/)
+[![Dependency Status](https://www.versioneye.com/user/projects/54b92f798d55087422000030/badge.svg?style=flat)](https://www.versioneye.com/user/projects/54b92f798d55087422000030)
+
+On Packagist:
+[![Latest Stable Version](https://poser.pugx.org/addwiki/mediawiki-api/version.png)](https://packagist.org/packages/addwiki/mediawiki-api)
+[![Download count](https://poser.pugx.org/addwiki/mediawiki-api/d/total.png)](https://packagist.org/packages/addwiki/mediawiki-api)
+
+Issue tracker: https://phabricator.wikimedia.org/project/profile/1490/
+
+## Installation
+
+Use composer to install the library and all its dependencies:
+
+ composer require "addwiki/mediawiki-api:~0.7.0"
+
+## Example Usage
+
+```php
+// Load all the stuff
+require_once( __DIR__ . '/vendor/autoload.php' );
+
+// Log in to a wiki
+$api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+$api->login( new \Mediawiki\Api\ApiUser( 'username', 'password' ) );
+$services = new \Mediawiki\Api\MediawikiFactory( $api );
+
+// Get a page
+$page = $services->newPageGetter()->getFromTitle( 'Foo' );
+
+// Edit a page
+$content = new \Mediawiki\DataModel\Content( 'New Text' );
+$revision = new \Mediawiki\DataModel\Revision( $content, $page->getPageIdentifier() );
+$services->newRevisionSaver()->save( $revision );
+
+// Move a page
+$services->newPageMover()->move(
+ $services->newPageGetter()->getFromTitle( 'FooBar' ),
+ new Title( 'FooBar' )
+);
+
+// Delete a page
+$services->newPageDeleter()->delete(
+ $services->newPageGetter()->getFromTitle( 'DeleteMe!' ),
+ array( 'reason' => 'Reason for Deletion' )
+);
+
+// Create a new page
+$newContent = new \Mediawiki\DataModel\Content( 'Hello World' );
+$title = new \Mediawiki\DataModel\Title( 'New Page' );
+$identifier = new \Mediawiki\DataModel\PageIdentifier( $title );
+$revision = new \Mediawiki\DataModel\Revision( $newContent, $identifier );
+$services->newRevisionSaver()->save( $revision );
+
+// List all pages in a category
+$pages = $services->newPageListGetter()->getPageListFromCategoryName( 'Category:Cat name' );
+```
+
+## Running the integration tests
+
+To run the integration tests, you need to have a running MediaWiki instance. The tests will create pages and categories without using a user account so it's best if you use a test instance. Furthermore you need to turn off rate limiting by adding the line
+
+ $wgGroupPermissions['*']['noratelimit'] = true;
+
+to the `LocalSettings.php` of your MediaWiki.
+
+By default, the tests will use the URL `http://localhost/w/api.php` as the API endpoint. If you have a different URL (e.g. `http://localhost:8080/w/api.php`), you need to configure the URL as an environemnt variable before running the tests. Example:
+
+ export MEDIAWIKI_API_URL='http://localhost:8080/w/api.php'
+
+**Warning:** Running the integration tests can take a long time to complete.
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md b/bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md
new file mode 100644
index 00000000..8d8bb9a1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/RELEASENOTES.md
@@ -0,0 +1,111 @@
+Release Notes
+=============
+
+These are the release notes for [mediawiki-api](http://addwiki.readthedocs.io/projects/mediawiki-api/).
+
+## Version 0.7.2 (20th November 2017)
+
+* New parent class for all API service classes,
+ with protected access on `Service::$api` to make it easier to subclass any services.
+* File uploading improved, with the option of [chunked uploading](https://www.mediawiki.org/wiki/API:Upload#Chunked_uploading).
+* Various fixes and improvements to the testing set-up and coding standards.
+
+## Version 0.7.1 (8th March 2017)
+
+* Fixed dependancy on addwiki/mediawiki-datamodel
+
+## Version 0.7 (March 2017)
+
+* Documentation! This package now has a
+ [dedicated documentation website](https://addwiki.readthedocs.io/projects/mediawiki-api/).
+* A new NamespaceGetter service with which you can get all namespaces,
+ or a single namespace by localised name, alias, or canonical name
+ ([#39](https://github.com/addwiki/mediawiki-api/pull/39), [#41](https://github.com/addwiki/mediawiki-api/pull/41)).
+* A new CategoryTraverser service for descending (all levels of) category trees
+ and either retrieving all pages or performing some action on each page.
+* A new method to PagePurger for purging multiple pages at once ([#36](https://github.com/addwiki/mediawiki-api/pull/36)).
+* All methods of the PageListGetter now continue their queries where the first request doesn't retrieve the whole result set
+ ([#31](https://github.com/addwiki/mediawiki-api/pull/31)).
+* Bug [#40](https://github.com/addwiki/mediawiki-api/pull/40) fixed with `RevisionSaver::save()` overwriting EditInfo if null.
+* Integration tests: more documentation about how to run integration tests locally,
+ and the tests are running on Travis CI.
+* Lots of fixes to coding-standards and in-code documentation.
+
+## Version 0.6 (3 August 2016)
+
+* Adds newParser method to factory
+* Use the new API continuation mode
+* Fix ignored bot assertion in EditInfo
+
+## Version 0.5.1 (7 September 2015)
+
+* Adds ApiGenerator interface
+* Adds AnonymousGenerator implementation of Generator
+* Adds FluentGenerator implementation of Generator
+
+## Version 0.5 (4 September 2015)
+
+####Breaks
+
+* LogListGetter now requires mediawiki verison 1.25 or above
+* PageListGetter now requires mediawiki verison 1.25 or above
+* Removed ALL Options objects
+
+####Additions
+
+* Introduces RevisionUndoer service
+* Introduces UserCreator service
+* Introduces FileUploader service
+* Introduces ImageRotator service
+
+####Libs
+
+* Using mediawiki-api-base 1.0
+* Using mediawiki-datamodel 0.6
+
+## Version 0.4 (13 January 2015)
+
+* Issue#8 PageListGetter methods now construct pages with a Title object rather than string
+* Page(Deleter|ListGetter|Mover|Protector|Restorer), User(Blocker|RightsChanger) service methods now require an *Options object rather than a selection of parameters.
+* Implemented PageListGetter::getRandom
+
+## Version 0.3 (2014-06-24)
+
+* Removes NewEditInfo and NewRevision
+* Moved basic api functionality to a separate base lib (mediawiki-api-base)
+* Repos renamed to Getters
+* PageGetter, RevisionSaver and UserGetter moved to the Service Namespace
+* Introduces MediawikiFactory
+* Introduces PageDeleter service
+* Introduces PageListGetter service
+* Introduces PageProtector service
+* Introduces PagePurger service
+* Introduces RevisionDeleter service
+* Introduces RevisionPatroller service
+* Introduces RevisionRollbacker service
+* Introduces UserBlocker service
+* Introduces UserRightsChanger service
+* Introduces PageRestorer service
+* Introduces RevisionRestorer service
+* Correctly handle non existent users in UserGetter
+
+
+## Version 0.2 (2014-02-23)
+
+* Altered everything for changed in mediawiki-datamodel
+* Removed Edit << action class
+* Introduces NewEditInfo and NewRevision
+
+
+## Version 0.1 (2014-02-23)
+
+Initial release with the following features:
+
+* MediawikiApi
+* ApiUser
+* MediawikiSession
+* UsageExceptions
+* PageRepo
+* UserRepo
+* EditSaver
+* Edit << action
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh b/bin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh
new file mode 100755
index 00000000..030830ad
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/bin/install-mediawiki.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+##
+## This script installs MediaWiki to ./build/mediawiki (relative to the directory from which it's called).
+##
+
+## Check inputs.
+if [ -z $MEDIAWIKI_VERSION ]; then
+ echo "You must specify the MEDIAWIKI_VERSION environment variable"
+ exit 0
+fi
+
+## Set some paths.
+BUILDDIR=$(cd $(dirname "$0"); pwd -P)"/../build"
+if [ ! -d $BUILDDIR ]; then
+ mkdir "$BUILDDIR"
+fi
+INSTALLDIR="$BUILDDIR/mediawiki"
+if [ -d "$INSTALLDIR" ]; then
+ rm -r "$INSTALLDIR"
+fi
+echo "Installing MediaWiki $MEDIAWIKI_VERSION to $INSTALLDIR"
+
+## Get the required version, and unpack it to `./build/mediawiki`.
+if [ ! -s "$BUILDDIR/$MEDIAWIKI_VERSION.tar.gz" ]; then
+ wget --directory-prefix="$BUILDDIR" "https://github.com/wikimedia/mediawiki/archive/$MEDIAWIKI_VERSION.tar.gz"
+fi
+cd "$BUILDDIR"
+echo "Unpacking"
+tar -zxf "$MEDIAWIKI_VERSION.tar.gz"
+mv "mediawiki-$MEDIAWIKI_VERSION" $INSTALLDIR
+
+## Install MediaWiki.
+cd "$INSTALLDIR"
+WIKIDB="test_wiki1"
+echo "Creating database as MySQL root user"
+PASSARG=""
+if [ -n "$DBPASS" ]; then
+ PASSARG="-p$DBPASS"
+fi
+mysql "$PASSARG" -uroot -e "DROP DATABASE IF EXISTS $WIKIDB"
+mysql "$PASSARG" -uroot -e "CREATE DATABASE $WIKIDB"
+echo "Updating dependencies (Composer)"
+composer install
+echo "Installing TestWiki1 wiki"
+php maintenance/install.php --dbtype mysql --dbuser "root" --dbpass "$DBPASS" --dbname $WIKIDB --scriptpath "" --pass admin123 TestWiki1 admin
+
+# Add some extra configuration to LocalSettings.php
+cat << 'EOF' >> "$INSTALLDIR/LocalSettings.php"
+$wgEnableUploads = true;
+$wgShowExceptionDetails = true;
+$wgCacheDirectory = __DIR__."/images/tmp";
+$wgServer = "http://127.0.0.1:8081";
+$wgUsePathInfo = false;
+$wgJobRunRate = 200;
+EOF
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/composer.json b/bin/wiki/vendor/addwiki/mediawiki-api/composer.json
new file mode 100644
index 00000000..f49a19ca
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "addwiki/mediawiki-api",
+ "type": "library",
+ "description": "A MediaWiki API library",
+ "keywords": ["Mediawiki"],
+ "license": "GPL-2.0+",
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "require": {
+ "addwiki/mediawiki-api-base": "~2.4",
+ "addwiki/mediawiki-datamodel": "~0.7.0"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "^0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "phpunit/phpunit": "~4.8",
+ "monolog/monolog": "^1.23"
+ },
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Mediawiki\\Api\\Test\\": ["tests/integration", "tests/unit"]
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "scripts": {
+ "fix": "phpcbf",
+ "test": [
+ "parallel-lint . --exclude vendor",
+ "phpcs -ps"
+ ]
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile b/bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile
new file mode 100644
index 00000000..46208266
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/Makefile
@@ -0,0 +1,225 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " epub3 to make an epub3"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+ @echo " dummy to check syntax errors of document sources"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mediawiki-api.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mediawiki-api.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/mediawiki-api"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mediawiki-api"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: epub3
+epub3:
+ $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+ @echo
+ @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+ $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+ @echo
+ @echo "Build finished. Dummy builder generates no files."
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst
new file mode 100644
index 00000000..497398e8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/category_traverser.rst
@@ -0,0 +1,28 @@
+Category Traversal
+==================
+
+The CategoryTraverser class is used to start at one Category page in a wiki's category hierarchy
+and descend through that category's children, grandchildren, and so on.
+The basic output of this is a Pages object containing all the pages in the category tree.
+It is also possible to register callbacks that will be called
+for every subcategory or other page (i.e. anything not a category).
+
+Basic usage
+-----------
+
+To get all pages in a category or any of its subcategories.
+
+.. code-block:: php
+ :linenos:
+
+ // Construct the API.
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $categoryTraverser = $services->newCategoryTraverser();
+
+ // Get the root category.
+ $rootCatIdent = new PageIdentifier( new Title( 'Category:Categories' ) );
+ $rootCat = $this->factory->newPageGetter()->getFromPageIdentifier( $pageIdentifier );
+
+ // Get all pages.
+ $allPages = $categoryTraverser->descend( $rootCat );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py b/bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py
new file mode 100644
index 00000000..3310f585
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/conf.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+import sys, os
+from sphinx.highlighting import lexers
+from pygments.lexers.web import PhpLexer
+
+lexers['php'] = PhpLexer(startinline=True, linenos=1)
+lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1)
+primary_domain = 'php'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'mediawiki-api'
+copyright = '2016, addwiki'
+author = 'addwiki'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.6'
+# The full version, including alpha/beta/rc tags.
+release = '0.6'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'default'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'mediawiki-apidoc'
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst
new file mode 100644
index 00000000..c2cd2813
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/contributing.rst
@@ -0,0 +1,44 @@
+Contributing
+============
+
+We welcome all contributions, be they code, documentation, or even just ideas about how to make this package better!
+
+The best way to get started is to browse the `#addwiki board on Phabricator`_
+and either work on one of the tasks already there or create a new one with details of what you want to work on.
+
+.. _Addwiki board on Phabricator: https://phabricator.wikimedia.org/tag/addwiki/
+
+Get the code
+------------
+
+The code is `hosted on GitHub`_. Clone the repository with::
+
+ $ git clone https://github.com/addwiki/mediawiki-api.git
+
+.. _hosted on GitHub: https://github.com/addwiki/mediawiki-api
+
+Run the tests
+-------------
+
+After cloning the repository and updating the dependencies with Composer,
+you should be able to run all **unit** tests with::
+
+ ./vendor/bin/phpunit ./tests/unit
+
+To run the **integration** tests you need to set up a local MediaWiki installation
+(including with a ``admin`` administrator user with password ``admin123``)
+and tell ``phpunit`` where to find it.
+
+1. Copy ``./phpunit.xml.dist`` to ``./phpunit.xml`` and add the following section::
+
+ <php>
+ <env name="MEDIAWIKI_API_URL" value="http://localhost/path/to/your/wiki/api.php" />
+ </php>
+
+2. Create and promote a new user::
+
+ $ php mediawiki/maintenance/createAndPromote.php --sysop WikiSysop wiki123sysop
+
+Now all integration tests can be run with::
+
+ ./vendor/bin/phpunit ./tests/integration
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst
new file mode 100755
index 00000000..9f8b534f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/file_uploader.rst
@@ -0,0 +1,28 @@
+Uploading files
+===============
+
+Basic usage
+-----------
+
+To upload a single, small-sized file:
+
+.. code-block:: php
+ :linenos:
+
+ // Construct the API.
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $fileUploader = $services->newFileUploader();
+
+ // Upload the file.
+ $fileUploader->upload( 'The_file.png', '/full/path/to/the_file.png' );
+
+If you need to work with larger files, you can switch to chunked uploading:
+
+.. code-block:: php
+ :linenos:
+
+ // Upload the file in 10 MB chunks.
+ $fileUploader = $services->newFileUploader();
+ $fileUploader->setChunkSize( 1024 * 1024 * 10 );
+ $fileUploader->upload( 'The_file.png', '/full/path/to/the_file.png' );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst
new file mode 100644
index 00000000..558107b3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/index.rst
@@ -0,0 +1,25 @@
+Documentation for addwiki/mediawiki-api
+=======================================
+
+Welcome to the documentation for the ``addwiki/mediawiki-api`` package!
+This is part of the Addwiki_ family of PHP packages.
+
+.. _Addwiki: http://addwiki.readthedocs.io/
+
+Quick links:
+
+* This documentation: http://addwiki.readthedocs.io/projects/mediawiki-api/
+* Source code: https://github.com/addwiki/mediawiki-api/
+* Issue tracker: https://phabricator.wikimedia.org/project/profile/1490/
+
+Contents
+--------
+
+.. toctree::
+ :maxdepth: 2
+
+ page_list_getter.rst
+ category_traverser.rst
+ namespace_getter.rst
+ file_uploader.rst
+ contributing.rst
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat b/bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat
new file mode 100644
index 00000000..a5507331
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/make.bat
@@ -0,0 +1,281 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. epub3 to make an epub3
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ echo. dummy to check syntax errors of document sources
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\mediawiki-api.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\mediawiki-api.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "epub3" (
+ %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+if "%1" == "dummy" (
+ %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. Dummy builder generates no files.
+ goto end
+)
+
+:end
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst
new file mode 100644
index 00000000..c362833f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/namespace_getter.rst
@@ -0,0 +1,54 @@
+Getting Namespaces
+==================
+
+The Name Space Getter allows you to search for namespaces and their aliases and to list all namespaces of a wiki.
+
+To use it, first get a new NamespaceGetter object from the factory:
+
+.. code-block:: php
+
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $namespaceGetter = $services->newNamespaceGetter();
+
+
+Looking for a namespace
+-----------------------
+
+If you've got a page name like ``File:awesome_cats.jpg`` and want to know its namespace ID and possible localized names
+and aliases, use the following code:
+
+.. code-block:: php
+
+ $fileNamespace = $namespaceGetter->getNamespaceByName( 'File' );
+ printf( "Name in local language: %s\n", $fileNamespace->getLocalName() );
+ printf( "Possible aliases: %s\n", implode( ', ', $fileNamespace->getAliases() ) );
+ // ... etc
+
+``getNamespaceByName`` accepts the canonical name, the local name and aliases. If you want to match only the canonical
+name, use ``getNamespaceByCanonicalName`` instead.
+
+
+Getting a namespaced page
+-------------------------
+
+If you have a page title that is not in the default namespace, you can't pass the page name string ``PageGetter`` but
+must construct a ``Title`` object instead:
+
+.. code-block:: php
+
+ $pageName = 'User:MalReynolds';
+ $nameParts = explode( ':', $pageName, 2 );
+ $namespace = $namespaceGetter->getNamespaceByName( $nameParts[0] );
+ $title = new \Mediawiki\DataModel\Title( $nameParts[1], $namespace->getId() );
+ $page = $services->newPageGetter()->getFromTitle( $title );
+
+
+Listing all namespaces
+----------------------
+
+.. code-block:: php
+
+ foreach( $namespaceGetter->getNamespaces() as $namespace ) {
+ echo $namespace->getLocalName() . "\n";
+ } \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst
new file mode 100644
index 00000000..aad9f8a8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_list_getter.rst
@@ -0,0 +1,88 @@
+Page Lists
+==========
+
+The Page List Getter allows you to retrieve lists of pages based on various criteria.
+It takes care of continuing queries where they span multiple requests,
+ensuring that you get all pages in your result set.
+This means that for some lists of pages a great many requests will be sent,
+and you should account for this possible performance problem when you request these lists
+(e.g. by running these as a background process and caching the results).
+
+To use it, first get a new PageListGetter object from the factory:
+
+.. code-block:: php
+
+ $api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+ $services = new \Mediawiki\Api\MediawikiFactory( $api );
+ $pageListGetter = $services->newPageListGetter();
+
+The examples below all use this ``$pageListGetter`` object.
+
+All methods of the PageListGetter return ``Page`` objects;
+this class is part of the `addwiki/mediawiki-datamodel`_ package,
+and is documented in `that page's documentation`_.
+
+.. _addwiki/mediawiki-datamodel: https://packagist.org/packages/addwiki/mediawiki-datamodel
+.. _that page's documentation: http://addwiki.readthedocs.io/projects/mediawiki-datamodel/
+
+All pages in a category
+-----------------------
+
+Note that the category name as provided should also include the 'Category' namespace prefix
+(in the language of the wiki, or in canonical English form).
+
+.. code-block:: php
+
+ $examplePages = $pageListGetter->getPageListFromCategoryName( 'Category:Example pages' );
+ foreach ( $examplePages->asArray() as $exPage ) {
+ echo $exPage->getTitle()->getText();
+ }
+
+Pages that transclude a template
+--------------------------------
+
+Although generally it is templates that are transcluded,
+any page may be and so any page title can be passed to this method.
+
+.. code-block:: php
+
+ $usingTestTemplate = $pageListGetter->getPageListFromPageTransclusions( 'Template:Test' );
+
+Pages that link to a given page
+-------------------------------
+
+Get the list of pages that link to a particular page.
+
+.. code-block:: php
+
+ $backLinks = $pageListGetter->getFromWhatLinksHere( 'Test page' );
+
+Pages with a given prefix
+-------------------------
+
+Find pages that have a particular prefix to their title.
+This can also be used to find subpages of any page.
+
+.. code-block:: php
+
+ $backLinks = $pageListGetter->getFromPrefix( 'A page/' );
+
+Random pages
+------------
+
+Get up to ten random pages at a time.
+This method takes the same arguments as the API `list=random`_ query.
+
+.. _list=random: https://www.mediawiki.org/wiki/API:Random
+
+* ``rnlimit`` How many pages to get. No more than 10 (20 for bots) allowed. Default: 1.
+* ``rnnamespace`` Pipe-separate list of namespace IDs.
+* ``rnfilterredir`` How to filter for redirects. Possible values: ``all``, ``redirects``, ``nonredirects``. Default: ``nonredirects``.
+
+.. code-block:: php
+
+ $backLinks = $pageListGetter->getRandom( [
+ 'rnlimit' => 7,
+ 'rnnamespace' => '3|5|6',
+ 'rnfilterredir' => 'all',
+ ] );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst
new file mode 100644
index 00000000..84cd7523
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/docs/page_purger.rst
@@ -0,0 +1,34 @@
+Page Purger
+===========
+
+``PagePurger`` allows you to purge a single ``Page`` or multiple ``Pages``.
+You can also check whether the ``Page`` or ``Pages`` have been purged successfully.
+
+To get started you need to create ``PagePurger`` object:
+
+.. code-block:: php
+
+$api = new \Mediawiki\Api\MediawikiApi( 'http://localhost/w/api.php' );
+$pagePurger = new \Mediawiki\Api\Service\PagePurger( $api );
+
+Purge
+-----
+
+Purge a single ``Page``. It will return a ``boolean`` that indicates if the purge operation was successful.
+
+Example:
+
+.. code-block:: php
+
+$page = new \Mediawiki\DataModel\Page(...);
+$pagePurger->purge( $page );
+
+PurgePages
+----------
+
+Purges every ``Page`` in the ``Pages`` object at once. It will return a new ``Pages`` object *with the purged ``Page``(s) only!*
+
+.. code-block:: php
+
+$pages = new \Mediawiki\DataModel\Pages(...);
+$pagePurger->purgePages( $pages );
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml b/bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml
new file mode 100644
index 00000000..1c5b15ce
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/phpcs.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<ruleset name="MediaWiki">
+ <rule ref="vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
+ <exclude name="MediaWiki.Commenting.FunctionComment.MissingParamComment" />
+ </rule>
+ <file>.</file>
+ <arg name="extensions" value="php,php5,inc"/>
+ <arg name="encoding" value="utf8"/>
+ <exclude-pattern>build/</exclude-pattern>
+ <exclude-pattern>vendor/</exclude-pattern>
+</ruleset>
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist
new file mode 100755
index 00000000..f674132b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/phpunit.xml.dist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- convertWarningsToExceptions is false as real API calls can return un expected warnings -->
+<phpunit
+ bootstrap="./vendor/autoload.php"
+ colors="true"
+ convertWarningsToExceptions="false"
+>
+ <testsuites>
+ <testsuite>
+ <directory suffix="Test.php">./tests/integration</directory>
+ </testsuite>
+ <testsuite>
+ <directory suffix="Test.php">./tests/unit</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./src</directory>
+ </whitelist>
+ </filter>
+</phpunit>
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php
new file mode 100644
index 00000000..9612f050
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/CategoryLoopException.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Mediawiki\DataModel\Pages;
+
+/**
+ * Class CategoryLoopException
+ * @package Mediawiki\Api
+ */
+class CategoryLoopException extends \Exception {
+
+ /** @var Pages */
+ protected $categoryPath;
+
+ /**
+ * @param Pages $path
+ */
+ public function setCategoryPath( Pages $path ) {
+ $this->categoryPath = $path;
+ }
+
+ /**
+ * Get the path of Pages that comprise the category loop. The first item in this list is also a
+ * child page of the last item.
+ * @return Pages The set of category Pages that comprise the category loop.
+ */
+ public function getCategoryPath() {
+ return $this->categoryPath;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php
new file mode 100644
index 00000000..715e3c02
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/AnonymousGenerator.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Mediawiki\Api\Generator;
+
+/**
+ * @access public
+ *
+ * @author Addshore
+ *
+ * @since 0.5.1
+ */
+class AnonymousGenerator implements ApiGenerator {
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var array
+ */
+ private $params;
+
+ /**
+ * @param string $name
+ * @param array $params including 'g' prefix keys
+ */
+ public function __construct( $name, array $params ) {
+ $this->name = $name;
+ $this->params = $params;
+ }
+
+ /**
+ * @return array
+ */
+ public function getParams() {
+ $params = $this->params;
+ $params['generator'] = $this->name;
+ return $params;
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php
new file mode 100644
index 00000000..923e98cd
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/ApiGenerator.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Mediawiki\Api\Generator;
+
+/**
+ * Interface relating to Mediawiki generators
+ * @see https://www.mediawiki.org/wiki/API:Query#Generators
+ *
+ * @access public
+ *
+ * @author Addshore
+ *
+ * @since 0.5.1
+ */
+interface ApiGenerator {
+
+ /**
+ * @since 0.5.1
+ *
+ * Associative array of parameters including the 'generator' parameter.
+ * All generator param keys must have their 'g' prefixes
+ *
+ * @return string[]
+ */
+ public function getParams();
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php
new file mode 100644
index 00000000..108f4999
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Generator/FluentGenerator.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Mediawiki\Api\Generator;
+
+/**
+ * @access public
+ *
+ * @author Addshore
+ *
+ * @since 0.5.1
+ */
+class FluentGenerator implements ApiGenerator {
+
+ private $name;
+ private $params;
+
+ /**
+ * @param string $name
+ */
+ public function __construct( $name ) {
+ $this->name = $name;
+ }
+
+ /**
+ * Convenience method for using this fluidly
+ *
+ * @param string $name
+ *
+ * @return FluentGenerator
+ */
+ public static function factory( $name ) {
+ return new self( $name );
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getParams() {
+ $params = $this->params;
+ $params['generator'] = $this->name;
+ return $params;
+ }
+
+ /**
+ * @param string $key optionally with the 'g' prefix
+ * @param string $value
+ *
+ * @return $this
+ */
+ public function set( $key, $value ) {
+ $key = $this->addKeyprefixIfNeeded( $key );
+ $this->params[$key] = $value;
+ return $this;
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return string
+ */
+ private function addKeyPrefixIfNeeded( $key ) {
+ if ( strtolower( substr( $key, 0, 1 ) ) === 'g' ) {
+ return $key;
+ }
+ return 'g' . $key;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php
new file mode 100644
index 00000000..fc773ce5
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/MediawikiFactory.php
@@ -0,0 +1,240 @@
+<?php
+
+namespace Mediawiki\Api;
+
+use Mediawiki\Api\Service\CategoryTraverser;
+use Mediawiki\Api\Service\FileUploader;
+use Mediawiki\Api\Service\ImageRotator;
+use Mediawiki\Api\Service\LogListGetter;
+use Mediawiki\Api\Service\NamespaceGetter;
+use Mediawiki\Api\Service\PageDeleter;
+use Mediawiki\Api\Service\PageListGetter;
+use Mediawiki\Api\Service\PageMover;
+use Mediawiki\Api\Service\PageProtector;
+use Mediawiki\Api\Service\PagePurger;
+use Mediawiki\Api\Service\PageGetter;
+use Mediawiki\Api\Service\PageRestorer;
+use Mediawiki\Api\Service\PageWatcher;
+use Mediawiki\Api\Service\Parser;
+use Mediawiki\Api\Service\RevisionDeleter;
+use Mediawiki\Api\Service\RevisionPatroller;
+use Mediawiki\Api\Service\RevisionRestorer;
+use Mediawiki\Api\Service\RevisionRollbacker;
+use Mediawiki\Api\Service\RevisionSaver;
+use Mediawiki\Api\Service\RevisionUndoer;
+use Mediawiki\Api\Service\UserBlocker;
+use Mediawiki\Api\Service\UserCreator;
+use Mediawiki\Api\Service\UserGetter;
+use Mediawiki\Api\Service\UserRightsChanger;
+
+/**
+ * @access public
+ *
+ * @author Addshore
+ */
+class MediawikiFactory {
+
+ /**
+ * @var MediawikiApi
+ */
+ private $api;
+
+ /**
+ * @param MediawikiApi $api
+ */
+ public function __construct( MediawikiApi $api ) {
+ $this->api = $api;
+ }
+
+ /**
+ * Get a new CategoryTraverser object for this API.
+ * @return \Mediawiki\Api\Service\CategoryTraverser
+ */
+ public function newCategoryTraverser() {
+ return new CategoryTraverser( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionSaver
+ */
+ public function newRevisionSaver() {
+ return new RevisionSaver( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return RevisionUndoer
+ */
+ public function newRevisionUndoer() {
+ return new RevisionUndoer( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageGetter
+ */
+ public function newPageGetter() {
+ return new PageGetter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return UserGetter
+ */
+ public function newUserGetter() {
+ return new UserGetter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageDeleter
+ */
+ public function newPageDeleter() {
+ return new PageDeleter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageMover
+ */
+ public function newPageMover() {
+ return new PageMover( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageListGetter
+ */
+ public function newPageListGetter() {
+ return new PageListGetter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageRestorer
+ */
+ public function newPageRestorer() {
+ return new PageRestorer( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PagePurger
+ */
+ public function newPagePurger() {
+ return new PagePurger( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionRollbacker
+ */
+ public function newRevisionRollbacker() {
+ return new RevisionRollbacker( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionPatroller
+ */
+ public function newRevisionPatroller() {
+ return new RevisionPatroller( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return PageProtector
+ */
+ public function newPageProtector() {
+ return new PageProtector( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return PageWatcher
+ */
+ public function newPageWatcher() {
+ return new PageWatcher( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionDeleter
+ */
+ public function newRevisionDeleter() {
+ return new RevisionDeleter( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return RevisionRestorer
+ */
+ public function newRevisionRestorer() {
+ return new RevisionRestorer( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return UserBlocker
+ */
+ public function newUserBlocker() {
+ return new UserBlocker( $this->api );
+ }
+
+ /**
+ * @since 0.3
+ * @return UserRightsChanger
+ */
+ public function newUserRightsChanger() {
+ return new UserRightsChanger( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return UserCreator
+ */
+ public function newUserCreator() {
+ return new UserCreator( $this->api );
+ }
+
+ /**
+ * @since 0.4
+ * @return LogListGetter
+ */
+ public function newLogListGetter() {
+ return new LogListGetter( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return FileUploader
+ */
+ public function newFileUploader() {
+ return new FileUploader( $this->api );
+ }
+
+ /**
+ * @since 0.5
+ * @return ImageRotator
+ */
+ public function newImageRotator() {
+ return new ImageRotator( $this->api );
+ }
+
+ /**
+ * @since 0.6
+ * @return Parser
+ */
+ public function newParser() {
+ return new Parser( $this->api );
+ }
+
+ /**
+ * @since 0.7
+ * @return NamespaceGetter
+ */
+ public function newNamespaceGetter() {
+ return new NamespaceGetter( $this->api );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php
new file mode 100644
index 00000000..c82b5d69
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/CategoryTraverser.php
@@ -0,0 +1,161 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\CategoryLoopException;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Pages;
+
+/**
+ * Category traverser.
+ *
+ * Note on spelling 'descendant' (from Wiktionary):
+ * The adjective, "descending from a biological ancestor", may be spelt either
+ * with an 'a' or with an 'e' in the final syllable. However the noun descendant,
+ * "one who is the progeny of someone", may be spelt only with an 'a'.
+ */
+class CategoryTraverser extends Service {
+
+ const CALLBACK_CATEGORY = 10;
+ const CALLBACK_PAGE = 20;
+
+ /**
+ * @var string[]
+ */
+ protected $namespaces;
+
+ /**
+ * @var callable[]
+ */
+ protected $callbacks;
+
+ /**
+ * Used to remember the previously-visited categories when traversing.
+ * @var string[]
+ */
+ protected $alreadyVisited;
+
+ /**
+ * @param MediawikiApi $api The API to connect to.
+ */
+ public function __construct( MediawikiApi $api ) {
+ parent::__construct( $api );
+ $this->callbacks = [];
+ }
+
+ /**
+ * Query the remote site for the list of namespaces in use, so that later we can tell what's a
+ * category and what's not. This populates $this->namespaces, and will not re-request on
+ * repeated invocations.
+ * @return void
+ */
+ protected function retrieveNamespaces() {
+ if ( is_array( $this->namespaces ) ) {
+ return;
+ }
+ $params = [ 'meta' => 'siteinfo', 'siprop' => 'namespaces' ];
+ $namespaces = $this->api->getRequest( new SimpleRequest( 'query', $params ) );
+ if ( isset( $namespaces['query']['namespaces'] ) ) {
+ $this->namespaces = $namespaces['query']['namespaces'];
+ }
+ }
+
+ /**
+ * Register a callback that will be called for each page or category visited during the
+ * traversal.
+ * @param int $type One of the 'CALLBACK_' constants of this class.
+ * @param callable $callback A callable that takes two \Mediawiki\DataModel\Page parameters.
+ */
+ public function addCallback( $type, $callback ) {
+ if ( !isset( $this->callbacks[$type] ) ) {
+ $this->callbacks[$type] = [];
+ }
+ $this->callbacks[$type][] = $callback;
+ }
+
+ /**
+ * Visit every descendant page of $rootCategoryName (which will be a Category
+ * page, because there are no desecendants of any other pages).
+ * @param Page $rootCat The full name of the page to start at.
+ * @param Page[] $currentPath Used only when recursing into this method, to track each path
+ * through the category hierarchy in case of loops.
+ * @return Pages All descendants of the given category.
+ * @throws CategoryLoopException If a category loop is detected.
+ */
+ public function descend( Page $rootCat, $currentPath = null ) {
+ // Make sure we know the namespace IDs.
+ $this->retrieveNamespaces();
+
+ $rootCatName = $rootCat->getPageIdentifier()->getTitle()->getText();
+ if ( is_null( $currentPath ) ) {
+ $this->alreadyVisited = [];
+ $currentPath = new Pages();
+ }
+ $this->alreadyVisited[] = $rootCatName;
+ $currentPath->addPage( $rootCat );
+
+ // Start a list of child pages.
+ $descendants = new Pages();
+ do {
+ $pageListGetter = new PageListGetter( $this->api );
+ $members = $pageListGetter->getPageListFromCategoryName( $rootCatName );
+ foreach ( $members->toArray() as $member ) {
+ /** @var Title */
+ $memberTitle = $member->getPageIdentifier()->getTitle();
+
+ // See if this page is a Category page.
+ $isCat = false;
+ if ( isset( $this->namespaces[ $memberTitle->getNs() ] ) ) {
+ $ns = $this->namespaces[ $memberTitle->getNs() ];
+ $isCat = ( isset( $ns['canonical'] ) && $ns['canonical'] === 'Category' );
+ }
+ // If it's a category, descend into it.
+ if ( $isCat ) {
+ // If this member has already been visited on this branch of the traversal,
+ // throw an Exception with information about which categories form the loop.
+ if ( $currentPath->hasPage( $member ) ) {
+ $currentPath->addPage( $member );
+ $loop = new CategoryLoopException();
+ $loop->setCategoryPath( $currentPath );
+ throw $loop;
+ }
+ // Don't go any further if we've already visited this member
+ // (does not indicate a loop, however; we've already caught that above).
+ if ( in_array( $memberTitle->getText(), $this->alreadyVisited ) ) {
+ continue;
+ }
+ // Call any registered callbacked, and carry on to the next branch.
+ $this->call( self::CALLBACK_CATEGORY, [ $member, $rootCat ] );
+ $newDescendants = $this->descend( $member, $currentPath );
+ $descendants->addPages( $newDescendants );
+ // Re-set the path.
+ $currentPath = new Pages();
+ } else {
+ // If it's a page, add it to the list and carry on.
+ $descendants->addPage( $member );
+ $this->call( self::CALLBACK_PAGE, [ $member, $rootCat ] );
+ }
+ }
+ } while ( isset( $result['continue'] ) );
+ return $descendants;
+ }
+
+ /**
+ * Call all the registered callbacks of a particular type.
+ * @param int $type The callback type; should match one of the 'CALLBACK_' constants.
+ * @param mixed[] $params The parameters to pass to the callback function.
+ */
+ protected function call( $type, $params ) {
+ if ( !isset( $this->callbacks[$type] ) ) {
+ return;
+ }
+ foreach ( $this->callbacks[$type] as $callback ) {
+ if ( is_callable( $callback ) ) {
+ call_user_func_array( $callback, $params );
+ }
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php
new file mode 100644
index 00000000..5ada5739
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/FileUploader.php
@@ -0,0 +1,139 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Exception;
+use Mediawiki\Api\MultipartRequest;
+use Mediawiki\Api\SimpleRequest;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class FileUploader extends Service {
+
+ /** @var int */
+ protected $chunkSize;
+
+ /**
+ * Set the chunk size used for chunked uploading.
+ *
+ * Chunked uploading is available in MediaWiki 1.20 and above, although prior to version 1.25,
+ * SVGs could not be uploaded via chunked uploading.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Upload#Chunked_uploading
+ *
+ * @param int $chunkSize In bytes.
+ */
+ public function setChunkSize( $chunkSize ) {
+ $this->chunkSize = $chunkSize;
+ }
+
+ /**
+ * Upload a file.
+ *
+ * @param string $targetName The name to give the file on the wiki (no 'File:' prefix required).
+ * @param string $location Can be local path or remote URL.
+ * @param string $text Initial page text for new files.
+ * @param string $comment Upload comment. Also used as the initial page text for new files if
+ * text parameter not provided.
+ * @param string $watchlist Unconditionally add or remove the page from your watchlist, use
+ * preferences or do not change watch. Possible values: 'watch', 'preferences', 'nochange'.
+ * @param bool $ignoreWarnings Ignore any warnings. This must be set to upload a new version of
+ * an existing image.
+ *
+ * @return bool
+ */
+ public function upload(
+ $targetName,
+ $location,
+ $text = '',
+ $comment = '',
+ $watchlist = 'preferences',
+ $ignoreWarnings = false
+ ) {
+ $params = [
+ 'filename' => $targetName,
+ 'token' => $this->api->getToken(),
+ ];
+ // Watchlist behaviour.
+ if ( in_array( $watchlist, [ 'watch', 'nochange' ] ) ) {
+ $params['watchlist'] = $watchlist;
+ }
+ // Ignore warnings?
+ if ( $ignoreWarnings ) {
+ $params['ignorewarnings'] = '1';
+ }
+ // Page text.
+ if ( !empty( $text ) ) {
+ $params['text'] = $text;
+ }
+ // Revision comment.
+ if ( !empty( $comment ) ) {
+ $params['comment'] = $comment;
+ }
+
+ if ( is_file( $location ) ) {
+ // Normal single-request upload.
+ $params['filesize'] = filesize( $location );
+ $params['file'] = fopen( $location, 'r' );
+ if ( is_int( $this->chunkSize ) && $this->chunkSize > 0 ) {
+ // Chunked upload.
+ $params = $this->uploadByChunks( $params );
+ }
+ } else {
+ // Upload from URL.
+ $params['url'] = $location;
+ }
+
+ $response = $this->api->postRequest( new SimpleRequest( 'upload', $params ) );
+ return ( $response['upload']['result'] === 'Success' );
+ }
+
+ /**
+ * Upload a file by chunks and get the parameters for the final upload call.
+ * @param mixed[] $params The request parameters.
+ * @return mixed[]
+ * @throws Exception
+ */
+ protected function uploadByChunks( $params ) {
+ // Get the file handle for looping, but don't keep it in the request parameters.
+ $fileHandle = $params['file'];
+ unset( $params['file'] );
+ // Track the chunks and offset.
+ $chunksDone = 0;
+ $params['offset'] = 0;
+ while ( true ) {
+
+ // 1. Make the request.
+ $params['chunk'] = fread( $fileHandle, $this->chunkSize );
+ $contentDisposition = 'form-data; name="chunk"; filename="' . $params['filename'] . '"';
+ $request = MultipartRequest::factory()
+ ->setParams( $params )
+ ->setAction( 'upload' )
+ ->setMultipartParams( [
+ 'chunk' => [ 'headers' => [ 'Content-Disposition' => $contentDisposition ] ],
+ ] );
+ $response = $this->api->postRequest( $request );
+
+ // 2. Deal with the response.
+ $chunksDone++;
+ $params['offset'] = ( $chunksDone * $this->chunkSize );
+ if ( !isset( $response['upload']['filekey'] ) ) {
+ // This should never happen. Even the last response still has the filekey.
+ throw new Exception( 'Unable to get filekey for chunked upload' );
+ }
+ $params['filekey'] = $response['upload']['filekey'];
+ if ( $response['upload']['result'] === 'Continue' ) {
+ // Amend parameters for next upload POST request.
+ $params['offset'] = $response['upload']['offset'];
+ } else {
+ // The final upload POST will be done in self::upload()
+ // to commit the upload out of the stash area.
+ unset( $params['chunk'], $params['offset'] );
+ return $params;
+ }
+ }
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php
new file mode 100644
index 00000000..ba5624dc
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/ImageRotator.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\Api\UsageException;
+use Mediawiki\DataModel\File;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class ImageRotator extends Service {
+
+ /**
+ * NOTE: This service has not been fully tested
+ *
+ * @param File $file
+ * @param int $rotation Degrees to rotate image clockwise, One value: 90, 180, 270
+ *
+ * @throws UsageException
+ * @return bool
+ */
+ public function rotate( File $file, $rotation ) {
+ $params = [
+ 'rotation' => $rotation,
+ 'token' => $this->api->getToken(),
+ ];
+
+ if ( !is_null( $file->getPageIdentifier()->getTitle() ) ) {
+ $params['titles'] = $file->getPageIdentifier()->getTitle()->getText();
+ } else {
+ $params['pageids'] = $file->getPageIdentifier()->getId();
+ }
+
+ $result = $this->api->postRequest( new SimpleRequest( 'imagerotate', $params ) );
+
+ // This module sometimes gives odd errors so deal with them..
+ if ( array_key_exists( 'imagerotate', $result ) ) {
+ $imageRotate = array_pop( $result['imagerotate'] );
+ if ( array_key_exists( 'result', $imageRotate ) &&
+ $imageRotate['result'] == 'Failure'
+ ) {
+ throw new UsageException(
+ 'imagerotate-Failure',
+ $imageRotate['errormessage'],
+ $result
+ );
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php
new file mode 100644
index 00000000..d9394919
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/LogListGetter.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Log;
+use Mediawiki\DataModel\LogList;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revisions;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Thomas Arrow
+ */
+class LogListGetter extends Service {
+
+ /**
+ * @param array $extraParams
+ *
+ * @return LogList
+ */
+ public function getLogList( array $extraParams = [] ) {
+ $logList = new LogList();
+
+ while ( true ) {
+ $params = [
+ 'list' => 'logevents',
+ 'leprop' => 'title|ids|type|user|timestamp|comment|details'
+ ];
+
+ $newParams = array_merge( $extraParams, $params );
+ $result = $this->api->getRequest( new SimpleRequest( 'query', $newParams ) );
+
+ foreach ( $result[ 'query' ]['logevents'] as $logevent ) {
+ $logList->addLog(
+ new Log(
+ $logevent['logid'],
+ $logevent['type'],
+ $logevent['action'],
+ $logevent['timestamp'],
+ $logevent['user'],
+ new Page(
+ new PageIdentifier(
+ new Title( $logevent['title'], $logevent['ns'] ),
+ $logevent['pageid']
+ ),
+ new Revisions()
+ ),
+ $logevent['comment'],
+ $this->getLogDetailsFromEvent( $logevent )
+ )
+ );
+ }
+
+ return $logList;
+ }
+ }
+
+ /**
+ * @param array $event
+ *
+ * @return array
+ */
+ private function getLogDetailsFromEvent( $event ) {
+ $ignoreKeys = array_flip( [
+ 'logid',
+ 'ns',
+ 'title',
+ 'pageid',
+ 'logpage',
+ 'type',
+ 'action',
+ 'user',
+ 'type',
+ 'timestamp',
+ 'comment' ] );
+ return array_diff_key( $event, $ignoreKeys );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php
new file mode 100644
index 00000000..c3f00030
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/NamespaceGetter.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\NamespaceInfo;
+
+/**
+ * @access private
+ *
+ * @author gbirke
+ */
+class NamespaceGetter extends Service {
+
+ /**
+ * Find a namespace by its canonical name
+ *
+ * @param string $canonicalName
+ * @return NamespaceInfo|null
+ */
+ public function getNamespaceByCanonicalName( $canonicalName ) {
+ $result = $this->getNamespaceResult()['query'];
+ foreach ( $result['namespaces'] as $nsInfo ) {
+ if ( !empty( $nsInfo['canonical'] ) && $nsInfo['canonical'] === $canonicalName ) {
+ return $this->createNamespaceFromQuery( $nsInfo, $result['namespacealiases'] );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find a namespace by its canonical name, local name or namespace alias
+ *
+ * @param string $name
+ * @return NamespaceInfo|null
+ */
+ public function getNamespaceByName( $name ) {
+ $result = $this->getNamespaceResult()['query'];
+ foreach ( $result['namespaces'] as $nsInfo ) {
+ if ( ( !empty( $nsInfo['canonical'] ) && $nsInfo['canonical'] === $name ) ||
+ $nsInfo['*'] === $name ) {
+ return $this->createNamespaceFromQuery( $nsInfo, $result['namespacealiases'] );
+ }
+ }
+ foreach ( $result['namespacealiases'] as $alias ) {
+ if ( $alias['*'] === $name && !empty( $result['namespaces'][$alias['id']] ) ) {
+ return $this->createNamespaceFromQuery(
+ $result['namespaces'][$alias['id']],
+ $result['namespacealiases']
+ );
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return NamespaceInfo[]
+ */
+ public function getNamespaces() {
+ $namespaces = [];
+ $result = $this->getNamespaceResult()['query'];
+ foreach ( $result['namespaces'] as $nsInfo ) {
+ $namespaces[$nsInfo['id']] = $this->createNamespaceFromQuery(
+ $nsInfo, $result['namespacealiases']
+ );
+ }
+ return $namespaces;
+ }
+
+ private function createNamespaceFromQuery( $nsInfo, $namespaceAliases ) {
+ return new NamespaceInfo(
+ $nsInfo['id'],
+ empty( $nsInfo['canonical'] ) ? '' : $nsInfo['canonical'],
+ $nsInfo['*'],
+ $nsInfo['case'],
+ empty( $nsInfo['defaultcontentmodel'] ) ? null : $nsInfo['defaultcontentmodel'],
+ $this->getAliases( $nsInfo['id'], $namespaceAliases )
+ );
+ }
+
+ /**
+ * @param int $id
+ * @param array $namespaceAliases Alias list, as returned by the API
+ * @return string[]
+ */
+ private function getAliases( $id, $namespaceAliases ) {
+ $aliases = [];
+ foreach ( $namespaceAliases as $alias ) {
+ if ( $alias['id'] === $id ) {
+ $aliases[] = $alias['*'];
+ }
+ }
+ return $aliases;
+ }
+
+ /**
+ * @return array
+ */
+ private function getNamespaceResult() {
+ return $this->api->getRequest( new SimpleRequest(
+ 'query', [
+ 'meta' => 'siteinfo',
+ 'siprop' => 'namespaces|namespacealiases'
+ ]
+ ) );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php
new file mode 100644
index 00000000..60c43e30
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageDeleter.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageDeleter extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param Page $page
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function delete( Page $page, array $extraParams = [] ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( $page->getPageIdentifier(), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Revision $revision
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function deleteFromRevision( Revision $revision, array $extraParams = [] ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( $revision->getPageIdentifier(), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param int $pageid
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function deleteFromPageId( $pageid, array $extraParams = [] ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( new PageIdentifier( null, $pageid ), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @since 0.5
+ *
+ * @param Title|string $title
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function deleteFromPageTitle( $title, array $extraParams = [] ) {
+ if ( is_string( $title ) ) {
+ $title = new Title( $title );
+ }
+ $this->api->postRequest( new SimpleRequest(
+ 'delete',
+ $this->getDeleteParams( new PageIdentifier( $title ), $extraParams )
+ ) );
+ return true;
+ }
+
+ /**
+ * @param PageIdentifier $identifier
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getDeleteParams( PageIdentifier $identifier, $extraParams ) {
+ $params = [];
+
+ if ( !is_null( $identifier->getId() ) ) {
+ $params['pageid'] = $identifier->getId();
+ } else {
+ $params['title'] = $identifier->getTitle()->getTitle();
+ }
+
+ $params['token'] = $this->api->getToken( 'delete' );
+
+ return array_merge( $extraParams, $params );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php
new file mode 100644
index 00000000..6c5c54cf
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageGetter.php
@@ -0,0 +1,246 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Content;
+use Mediawiki\DataModel\EditInfo;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Revisions;
+use Mediawiki\DataModel\Title;
+use RuntimeException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageGetter extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param int $id
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromRevisionId( $id, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'revids' => $id ], $extraParams )
+ )
+ );
+
+ return $this->newPageFromResult( array_shift( $result['query']['pages'] ) );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param string|Title $title
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromTitle( $title, array $extraParams = [] ) {
+ if ( $title instanceof Title ) {
+ $title = $title->getTitle();
+ }
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'titles' => $title ], $extraParams )
+ )
+ );
+
+ return $this->newPageFromResult( array_shift( $result['query']['pages'] ) );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param int $id
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromPageId( $id, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'pageids' => $id ], $extraParams )
+ )
+ );
+
+ return $this->newPageFromResult( array_shift( $result['query']['pages'] ) );
+ }
+
+ /**
+ * @since 0.4
+ *
+ * @param PageIdentifier $pageIdentifier
+ * @param array $extraParams
+ *
+ * @throws RuntimeException
+ * @return Page
+ */
+ public function getFromPageIdentifier(
+ PageIdentifier $pageIdentifier,
+ array $extraParams = []
+ ) {
+ if ( !$pageIdentifier->identifiesPage() ) {
+ throw new RuntimeException( '$pageIdentifier does not identify a page' );
+ }
+ if ( !is_null( $pageIdentifier->getId() ) ) {
+ return $this->getFromPageId( $pageIdentifier->getId(), $extraParams );
+ } else {
+ return $this->getFromTitle( $pageIdentifier->getTitle(), $extraParams );
+ }
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Page $page
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromPage( Page $page, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'pageids' => $page->getId() ], $extraParams )
+ )
+ );
+ $revisions = $this->getRevisionsFromResult( array_shift( $result['query']['pages'] ) );
+ $revisions->addRevisions( $page->getRevisions() );
+
+ return new Page(
+ $page->getPageIdentifier(),
+ $revisions
+ );
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param Revision $revision
+ * @param array $extraParams
+ *
+ * @return Page
+ */
+ public function getFromRevision( Revision $revision, array $extraParams = [] ) {
+ $result =
+ $this->api->getRequest(
+ new SimpleRequest(
+ 'query',
+ $this->getQuery( [ 'revids' => $revision->getId() ], $extraParams )
+ )
+ );
+ $revisions = $this->getRevisionsFromResult( array_shift( $result['query']['pages'] ) );
+ $revisions->addRevision( $revision );
+
+ return new Page(
+ new PageIdentifier(
+ new Title(
+ $result['title'],
+ $result['ns']
+ ),
+ $result['pageid']
+ ),
+ $revisions
+ );
+ }
+
+ /**
+ * @param array $additionalParams
+ *
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getQuery( $additionalParams, array $extraParams = [] ) {
+ $base = [
+ 'prop' => 'revisions|info|pageprops',
+ 'rvprop' => 'ids|flags|timestamp|user|size|sha1|comment|content|tags',
+ 'inprop' => 'protection',
+ ];
+
+ return array_merge( $extraParams, $base, $additionalParams );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return Revisions
+ */
+ private function getRevisionsFromResult( $array ) {
+ $revisions = new Revisions();
+ $pageid = $array['pageid'];
+ foreach ( $array['revisions'] as $revision ) {
+ $revisions->addRevision(
+ new Revision(
+ $this->getContent( $array['contentmodel'], $revision['*'] ),
+ new PageIdentifier( new Title( $array['title'], $array['ns'] ), $pageid ),
+ $revision['revid'],
+ new EditInfo(
+ $revision['comment'],
+ array_key_exists( 'minor', $revision ),
+ array_key_exists( 'bot', $revision )
+ ),
+ $revision['user'],
+ $revision['timestamp']
+ )
+ );
+ }
+
+ return $revisions;
+ }
+
+ /**
+ * @param string $model
+ * @param string $content returned from the API
+ *
+ * @throws RuntimeException
+ * @return Content
+ */
+ private function getContent( $model, $content ) {
+ return new Content( $content, $model );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return Page
+ */
+ private function newPageFromResult( $array ) {
+ if ( array_key_exists( 'pageid', $array ) ) {
+ $pageid = $array['pageid'];
+ $revisions = $this->getRevisionsFromResult( $array );
+ } else {
+ $pageid = 0;
+ $revisions = new Revisions();
+ }
+
+ return new Page(
+ new PageIdentifier(
+ new Title(
+ $array['title'],
+ $array['ns']
+ ),
+ $pageid
+ ),
+ $revisions
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php
new file mode 100644
index 00000000..6b6d0007
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageListGetter.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Pages;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageListGetter extends Service {
+
+ /**
+ * Get the set of pages in a given category. Extra parameters can include:
+ * cmtype: default 'page|subcat|file'
+ * cmlimit: default 10, maximum 500 (5000 for bots)
+ *
+ * @link https://www.mediawiki.org/wiki/API:Categorymembers
+ * @since 0.3
+ *
+ * @param string $name
+ * @param array $extraParams
+ *
+ * @return Pages
+ */
+ public function getPageListFromCategoryName( $name, array $extraParams = [] ) {
+ $params = array_merge( $extraParams, [
+ 'list' => 'categorymembers',
+ 'cmtitle' => $name,
+ ] );
+ return $this->runQuery( $params, 'cmcontinue', 'categorymembers' );
+ }
+
+ /**
+ * List pages that transclude a certain page.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Embeddedin
+ * @since 0.5
+ *
+ * @param string $pageName
+ * @param array $extraParams
+ *
+ * @return Pages
+ */
+ public function getPageListFromPageTransclusions( $pageName, array $extraParams = [] ) {
+ $params = array_merge( $extraParams, [
+ 'list' => 'embeddedin',
+ 'eititle' => $pageName,
+ ] );
+ return $this->runQuery( $params, 'eicontinue', 'embeddedin' );
+ }
+
+ /**
+ * Get all pages that link to the given page.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Linkshere
+ * @since 0.5
+ * @uses PageListGetter::runQuery()
+ *
+ * @param string $pageName The page name
+ * @param string[] Any extra parameters to use: lhprop, lhnamespace, lhshow, lhlimit
+ *
+ * @return Pages
+ */
+ public function getFromWhatLinksHere( $pageName, $extraParams = [] ) {
+ $params = array_merge( $extraParams, [
+ 'prop' => 'info',
+ 'generator' => 'linkshere',
+ 'titles' => $pageName,
+ ] );
+ return $this->runQuery( $params, 'glhcontinue', 'pages' );
+ }
+
+ /**
+ * Get all pages that have the given prefix.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Allpages
+ *
+ * @param string $prefix The page title prefix.
+ *
+ * @return Pages
+ */
+ public function getFromPrefix( $prefix ) {
+ $params = [
+ 'list' => 'allpages',
+ 'apprefix' => $prefix,
+ ];
+ return $this->runQuery( $params, 'apcontinue', 'allpages' );
+ }
+
+ /**
+ * Get up to 10 random pages.
+ *
+ * @link https://www.mediawiki.org/wiki/API:Random
+ * @uses PageListGetter::runQuery()
+ *
+ * @param array $extraParams
+ *
+ * @return Pages
+ */
+ public function getRandom( array $extraParams = [] ) {
+ $params = array_merge( $extraParams, [ 'list' => 'random' ] );
+ return $this->runQuery( $params, null, 'random', 'id', false );
+ }
+
+ /**
+ * Run a query to completion.
+ *
+ * @param string[] $params Query parameters
+ * @param string $contName Result subelement name for continue details
+ * @param string $resName Result element name for main results array
+ * @param string $pageIdName Result element name for page ID
+ * @param bool $cont Whether to continue the query, using multiple requests
+ * @return Pages
+ */
+ protected function runQuery( $params, $contName, $resName, $pageIdName = 'pageid', $cont = true ) {
+ $pages = new Pages();
+
+ do {
+ // Set up continue parameter if it's been set already.
+ if ( isset( $result['continue'][$contName] ) ) {
+ $params[$contName] = $result['continue'][$contName];
+ }
+
+ // Run the actual query.
+ $result = $this->api->getRequest( new SimpleRequest( 'query', $params ) );
+ if ( !array_key_exists( 'query', $result ) ) {
+ return $pages;
+ }
+
+ // Add the results to the output page list.
+ foreach ( $result['query'][$resName] as $member ) {
+ $pageTitle = new Title( $member['title'], $member['ns'] );
+ $page = new Page( new PageIdentifier( $pageTitle, $member[$pageIdName] ) );
+ $pages->addPage( $page );
+ }
+
+ } while ( $cont && isset( $result['continue'] ) );
+
+ return $pages;
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php
new file mode 100644
index 00000000..f7eba2af
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageMover.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageMover extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param Page $page
+ * @param Title $target
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function move( Page $page, Title $target, array $extraParams = [] ) {
+ $this->api->postRequest(
+ new SimpleRequest(
+ 'move', $this->getMoveParams( $page->getId(), $target, $extraParams )
+ )
+ );
+
+ return true;
+ }
+
+ /**
+ * @since 0.2
+ *
+ * @param int $pageid
+ * @param Title $target
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function moveFromPageId( $pageid, Title $target, array $extraParams = [] ) {
+ $this->api->postRequest(
+ new SimpleRequest( 'move', $this->getMoveParams( $pageid, $target, $extraParams ) )
+ );
+
+ return true;
+ }
+
+ /**
+ * @param int $pageid
+ * @param Title $target
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getMoveParams( $pageid, $target, $extraParams ) {
+ $params = [];
+ $params['fromid'] = $pageid;
+ $params['to'] = $target->getTitle();
+ $params['token'] = $this->api->getToken( 'move' );
+
+ return array_merge( $extraParams, $params );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php
new file mode 100644
index 00000000..e3988e84
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageProtector.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use InvalidArgumentException;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageProtector extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Page $page
+ * @param string[] $protections where the 'key' is the action and the 'value' is the group
+ * @param array $extraParams
+ *
+ * @return bool
+ * @throws InvalidArgumentException
+ */
+ public function protect( Page $page, $protections, array $extraParams = [] ) {
+ if ( !is_array( $protections ) || empty( $protections ) ) {
+ throw new InvalidArgumentException(
+ '$protections must be an array with keys and values'
+ );
+ }
+
+ $params = [
+ 'pageid' => $page->getId(),
+ 'token' => $this->api->getToken( 'protect' ),
+ ];
+ $protectionsString = '';
+ foreach ( $protections as $action => $value ) {
+ if ( !is_string( $action ) || !is_string( $value ) ) {
+ throw new InvalidArgumentException(
+ 'All keys and elements of $protections must be strings'
+ );
+ }
+ $protectionsString = $action . '=' . $value . '|';
+ }
+ $params['protections'] = rtrim( $protectionsString, '|' );
+
+ $this->api->postRequest(
+ new SimpleRequest( 'protect', array_merge( $extraParams, $params ) )
+ );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php
new file mode 100644
index 00000000..6f9057f1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PagePurger.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\Generator\ApiGenerator;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Pages;
+use Mediawiki\DataModel\Page;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ * @author Thomas Arrow
+ */
+class PagePurger extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @brief Purge a single page
+ *
+ * Purges a single page by submitting a
+ * 'purge' action to the mediawiki api
+ * with the parameter 'pageids' set to
+ * the singe page id
+ *
+ * @param Page $page the page that is going to be purged
+ *
+ * @return bool return true if the purge was successful
+ */
+ public function purge( Page $page ) {
+ $responseArray = $this->api->postRequest(
+ new SimpleRequest( 'purge', [ 'pageids' => $page->getId() ] )
+ );
+
+ // the purge response for the page
+ $purgeResponse = $responseArray['purge'][0];
+
+ return array_key_exists( 'purged', $purgeResponse );
+ }
+
+ /**
+ * @since 0.7
+ *
+ * @brief Purge multiple pages
+ *
+ * Purges all the pages of the Pages object
+ * by submitting a 'purge' action to the mediawiki
+ * api with the parameter 'pageids' set to be the
+ * pages ids in multiple-value seperation.
+ *
+ * @param Pages $pages the pages that are going to be purged
+ *
+ * @return Pages the pages that have been purged successfully
+ */
+ public function purgePages( Pages $pages ) {
+ $pagesArray = $pages->toArray();
+ $pagesIds = [];
+
+ foreach ( $pagesArray as $page ) {
+ array_push( $pagesIds, $page->getId() );
+ }
+
+ // convert an array to multiple-value format
+ // because the mediawiki api require multiple
+ // values to be seperated like the example
+ // ex: [111, 222, 333] => "111|222|333"
+ $pageIdsMultiple = implode( '|', $pagesIds );
+
+ $responseArray = $this->api->postRequest(
+ new SimpleRequest( 'purge', [ 'pageids' => $pageIdsMultiple ] )
+ );
+
+ // array that will hold the successfully purged pages
+ $purgedPages = new Pages();
+
+ // for every purge result
+ foreach ( $responseArray['purge'] as $purgeResponse ) {
+ // if the purge for the page was successful
+ if ( array_key_exists( 'purged', $purgeResponse ) ) {
+ // we iterate all the input pages
+ foreach ( $pagesArray as $page ) {
+ // and if the page from the input was successfully purged
+ if ( $purgeResponse['title'] === $page->getTitle()->getText() ) {
+ // add it in the purgedPages object
+ $purgedPages->addPage( $page );
+
+ break;
+ }
+
+ }
+
+ }
+
+ }
+
+ return $purgedPages;
+ }
+
+ /**
+ * @since 0.6
+ *
+ * @param ApiGenerator $generator
+ *
+ * @return bool
+ */
+ public function purgeGenerator( ApiGenerator $generator ) {
+ $this->api->postRequest(
+ new SimpleRequest( 'purge', $generator->getParams() )
+ );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php
new file mode 100644
index 00000000..a422bd97
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageRestorer.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Title;
+use OutOfBoundsException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageRestorer extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Page $page
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function restore( Page $page, array $extraParams = [] ) {
+ $this->api->postRequest(
+ new SimpleRequest(
+ 'undelete',
+ $this->getUndeleteParams( $page->getTitle(), $extraParams )
+ )
+ );
+
+ return true;
+ }
+
+ /**
+ * @param Title $title
+ * @param array $extraParams
+ *
+ * @return array
+ */
+ private function getUndeleteParams( Title $title, $extraParams ) {
+ $params = [];
+
+ $params['title'] = $title->getTitle();
+ $params['token'] = $this->getUndeleteToken( $title );
+
+ return array_merge( $extraParams, $params );
+ }
+
+ /**
+ * @param Title $title
+ *
+ * @throws OutOfBoundsException
+ * @returns string
+ */
+ private function getUndeleteToken( Title $title ) {
+ $response = $this->api->postRequest(
+ new SimpleRequest(
+ 'query', [
+ 'list' => 'deletedrevs',
+ 'titles' => $title->getTitle(),
+ 'drprop' => 'token',
+ ]
+ )
+ );
+ if ( array_key_exists( 'token', $response['query']['deletedrevs'][0] ) ) {
+ return $response['query']['deletedrevs'][0]['token'];
+ } else {
+ throw new OutOfBoundsException(
+ 'Could not get page undelete token from list=deletedrevs query'
+ );
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php
new file mode 100644
index 00000000..e7afab17
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/PageWatcher.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class PageWatcher extends Service {
+
+ /**
+ * @param Page $page
+ *
+ * @return bool
+ */
+ public function watch( Page $page ) {
+ $params = [
+ 'token' => $this->api->getToken( 'watch' ),
+ ];
+ if ( !is_null( $page->getPageIdentifier()->getId() ) ) {
+ $params['pageids'] = $page->getPageIdentifier()->getId();
+ } elseif ( !is_null( $page->getPageIdentifier()->getTitle() ) ) {
+ $params['titles'] = $page->getPageIdentifier()->getTitle()->getTitle();
+ } elseif ( !is_null( $page->getRevisions()->getLatest() ) ) {
+ $params['revids'] = $page->getRevisions()->getLatest()->getId();
+ }
+
+ $this->api->postRequest( new SimpleRequest( 'watch', $params ) );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php
new file mode 100644
index 00000000..da83e39e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Parser.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\PageIdentifier;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class Parser extends Service {
+
+ /**
+ * @param PageIdentifier $pageIdentifier
+ *
+ * @return array the parse result (raw from the api)
+ */
+ public function parsePage( PageIdentifier $pageIdentifier ) {
+ return $this->parsePageAsync( $pageIdentifier )->wait();
+ }
+
+ /**
+ * @param PageIdentifier $pageIdentifier
+ *
+ * @return PromiseInterface of array the parse result (raw from the api)
+ */
+ public function parsePageAsync( PageIdentifier $pageIdentifier ) {
+ $params = [];
+ if ( $pageIdentifier->getId() !== null ) {
+ $params['pageid'] = $pageIdentifier->getId();
+ } elseif ( $pageIdentifier->getTitle() !== null ) {
+ $params['page'] = $pageIdentifier->getTitle()->getText();
+ } else {
+ throw new \RuntimeException( 'No way to identify page' );
+ }
+
+ $promise = $this->api->getRequestAsync( new SimpleRequest( 'parse', $params ) );
+
+ return $promise->then( function ( $result ) {
+ return $result['parse'];
+ } );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php
new file mode 100644
index 00000000..b12a7278
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionDeleter.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionDeleter extends Service {
+
+ /**
+ * @since 0.5
+ *
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function delete( Revision $revision ) {
+ $params = [
+ 'type' => 'revision',
+ 'hide' => 'content',
+ // Note: pre 1.24 this is a delete token, post it is csrf
+ 'token' => $this->api->getToken( 'delete' ),
+ 'ids' => $revision->getId(),
+ ];
+
+ $this->api->postRequest( new SimpleRequest(
+ 'revisiondelete',
+ $params
+ ) );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php
new file mode 100644
index 00000000..e642fd96
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionPatroller.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionPatroller extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Revision $revision
+ *
+ * @return bool success
+ */
+ public function patrol( Revision $revision ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'patrol', [
+ 'revid' => $revision->getId(),
+ 'token' => $this->getTokenForRevision( $revision ),
+ ] ) );
+ return true;
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @returns string
+ */
+ private function getTokenForRevision( Revision $revision ) {
+ $result = $this->api->postRequest( new SimpleRequest( 'query', [
+ 'list' => 'recentchanges',
+ 'rcstart' => $revision->getTimestamp(),
+ 'rcend' => $revision->getTimestamp(),
+ 'rctoken' => 'patrol',
+ ] ) );
+ $result = array_shift( $result['query']['recentchanges'] );
+ return $result['patroltoken'];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php
new file mode 100644
index 00000000..fb82c252
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRestorer.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionRestorer extends Service {
+
+ /**
+ * @since 0.5
+ *
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function restore( Revision $revision ) {
+ $params = [
+ 'type' => 'revision',
+ 'show' => 'content',
+ // Note: pre 1.24 this is a delete token, post it is csrf
+ 'token' => $this->api->getToken( 'delete' ),
+ 'ids' => $revision->getId(),
+ ];
+
+ $this->api->postRequest( new SimpleRequest(
+ 'revisiondelete',
+ $params
+ ) );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php
new file mode 100644
index 00000000..76a2f5c7
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionRollbacker.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionRollbacker extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param Revision $revision
+ * @param Title $title if using MW 1.24 of lower (https://gerrit.wikimedia.org/r/#/c/133063/)
+ *
+ * @return bool
+ */
+ public function rollback( Revision $revision, Title $title = null ) {
+ $this->api->postRequest(
+ new SimpleRequest( 'rollback', $this->getRollbackParams( $revision, $title ) )
+ );
+
+ return true;
+ }
+
+ /**
+ * @param Revision $revision
+ * @param Title|null $title
+ *
+ * @return array
+ */
+ private function getRollbackParams( Revision $revision, $title ) {
+ $params = [];
+ if ( !is_null( $title ) ) {
+ // This is needed prior to https://gerrit.wikimedia.org/r/#/c/133063/
+ $params['title'] = $title->getTitle();
+ } else {
+ // This will work after https://gerrit.wikimedia.org/r/#/c/133063/
+ $params['pageid'] = $revision->getPageId();
+ }
+ $params['user'] = $revision->getUser();
+ $params['token'] = $this->getTokenForRevision( $revision );
+
+ return $params;
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @returns string
+ */
+ private function getTokenForRevision( Revision $revision ) {
+ $result = $this->api->postRequest(
+ new SimpleRequest(
+ 'query', [
+ 'prop' => 'revisions',
+ 'revids' => $revision->getId(),
+ 'rvtoken' => 'rollback',
+ ]
+ )
+ );
+ $result = array_shift( $result['query']['pages'] );
+
+ return $result['revisions'][0]['rollbacktoken'];
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php
new file mode 100644
index 00000000..00b91ee6
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionSaver.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\EditInfo;
+use Mediawiki\DataModel\Revision;
+use RuntimeException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ * @author DFelten (EditInfo fix)
+ */
+class RevisionSaver extends Service {
+
+ /**
+ * @since 0.2
+ *
+ * @param Revision $revision
+ * @param EditInfo $editInfo
+ *
+ * @return bool success
+ */
+ public function save( Revision $revision, EditInfo $editInfo = null ) {
+ $editInfo = $editInfo ? $editInfo : $revision->getEditInfo();
+
+ $result = $this->api->postRequest(
+ new SimpleRequest( 'edit', $this->getEditParams( $revision, $editInfo ) )
+ );
+ return ( $result['edit']['result'] == 'Success' );
+ }
+
+ /**
+ * @param Revision $revision
+ * @param EditInfo $editInfo
+ *
+ * @throws RuntimeException
+ * @returns array
+ */
+ private function getEditParams( Revision $revision, EditInfo $editInfo = null ) {
+ if ( !$revision->getPageIdentifier()->identifiesPage() ) {
+ throw new RuntimeException( '$revision PageIdentifier does not identify a page' );
+ }
+
+ $params = [];
+
+ $content = $revision->getContent();
+ $data = $content->getData();
+ if ( !is_string( $data ) ) {
+ throw new RuntimeException( 'Dont know how to save content of this model.' );
+ }
+ $params['text'] = $content->getData();
+ $params['md5'] = md5( $content->getData() );
+
+ $timestamp = $revision->getTimestamp();
+ if ( !is_null( $timestamp ) ) {
+ $params['basetimestamp'] = $timestamp;
+ }
+
+ if ( !is_null( $revision->getPageIdentifier()->getId() ) ) {
+ $params['pageid'] = $revision->getPageIdentifier()->getId();
+ } else {
+ $params['title'] = $revision->getPageIdentifier()->getTitle()->getTitle();
+ }
+
+ $params['token'] = $this->api->getToken();
+
+ if ( $this->api->isLoggedin() ) {
+ $params['assert'] = 'user';
+ }
+
+ $this->addEditInfoParams( $editInfo, $params );
+
+ return $params;
+ }
+
+ /**
+ * @param null|EditInfo $editInfo
+ * @param array &$params
+ */
+ private function addEditInfoParams( $editInfo, &$params ) {
+ if ( !is_null( $editInfo ) ) {
+ $params['summary'] = $editInfo->getSummary();
+ if ( $editInfo->getMinor() ) {
+ $params['minor'] = true;
+ }
+ if ( $editInfo->getBot() ) {
+ $params['bot'] = true;
+ $params['assert'] = 'bot';
+ }
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php
new file mode 100644
index 00000000..e411030e
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/RevisionUndoer.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class RevisionUndoer extends Service {
+
+ /**
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function undo( Revision $revision ) {
+ $this->api->postRequest( new SimpleRequest(
+ 'edit',
+ $this->getParamsFromRevision( $revision )
+ ) );
+ return true;
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @return array
+ */
+ private function getParamsFromRevision( Revision $revision ) {
+ $params = [
+ 'undo' => $revision->getId(),
+ 'token' => $this->api->getToken(),
+ ];
+
+ if ( !is_null( $revision->getPageIdentifier()->getId() ) ) {
+ $params['pageid'] = $revision->getPageIdentifier()->getId();
+ } else {
+ $params['title'] = $revision->getPageIdentifier()->getTitle()->getTitle();
+ }
+
+ return $params;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php
new file mode 100644
index 00000000..4a4b466b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/Service.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace MediaWiki\Api\Service;
+
+use Mediawiki\Api\MediawikiApi;
+
+/**
+ * The base service functions that all services inherit.
+ * @since 0.7.2
+ */
+abstract class Service {
+
+ /** @var MediawikiApi */
+ protected $api;
+
+ /**
+ * @param MediawikiApi $api The API to in for this service.
+ */
+ public function __construct( MediawikiApi $api ) {
+ $this->api = $api;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php
new file mode 100644
index 00000000..9cf9f421
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserBlocker.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use InvalidArgumentException;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\User;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserBlocker extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param User|string $user
+ * @param array $extraParams
+ *
+ * @throws InvalidArgumentException
+ * @return bool
+ */
+ public function block( $user, array $extraParams = [] ) {
+ if ( !$user instanceof User && !is_string( $user ) ) {
+ throw new InvalidArgumentException( '$user must be either a string or User object' );
+ }
+
+ if ( $user instanceof User ) {
+ $user = $user->getName();
+ }
+
+ $params = [
+ 'user' => $user,
+ 'token' => $this->api->getToken( 'block' ),
+ ];
+
+ $params = array_merge( $extraParams, $params );
+
+ $this->api->postRequest( new SimpleRequest( 'block', $params ) );
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php
new file mode 100644
index 00000000..8d74b107
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserCreator.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use InvalidArgumentException;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\Api\UsageException;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserCreator extends Service {
+
+ /**
+ * @param string $username
+ * @param string $password
+ * @param string|null $email
+ *
+ * @return bool
+ */
+ public function create( $username, $password, $email = null ) {
+ if ( !is_string( $username ) ) {
+ throw new InvalidArgumentException( '$username should be a string' );
+ }
+ if ( !is_string( $password ) ) {
+ throw new InvalidArgumentException( '$password should be a string' );
+ }
+ if ( !is_string( $email ) && !is_null( $email ) ) {
+ throw new InvalidArgumentException( '$email should be a string or null' );
+ }
+
+ $params = [
+ 'createreturnurl' => $this->api->getApiUrl(),
+ 'createtoken' => $this->api->getToken( 'createaccount' ),
+ 'username' => $username,
+ 'password' => $password,
+ 'retype' => $password,
+ ];
+
+ if ( !is_null( $email ) ) {
+ $params['email'] = $email;
+ }
+
+ try {
+ $result = $this->api->postRequest( new SimpleRequest( 'createaccount', $params ) );
+ return $result['createaccount']['status'] === 'PASS';
+ } catch ( UsageException $exception ) {
+ // If the above request failed, try again in the old way.
+ if ( $exception->getApiCode() === 'noname' ) {
+ return $this->createPreOneTwentySeven( $params );
+ }
+ throw $exception;
+ }
+ }
+
+ /**
+ * Create a user in the pre 1.27 manner.
+ * @link https://www.mediawiki.org/wiki/API:Account_creation/pre-1.27
+ * @return bool
+ */
+ protected function createPreOneTwentySeven( $params ) {
+ $newParams = [
+ 'name' => $params['username'],
+ 'password' => $params['password'],
+ ];
+ if ( array_key_exists( 'email', $params ) ) {
+ $newParams['email'] = $params['email'];
+ }
+ // First get the token.
+ $tokenRequest = new SimpleRequest( 'createaccount', $newParams );
+ $result = $this->api->postRequest( $tokenRequest );
+ if ( $result['createaccount']['result'] == 'NeedToken' ) {
+ // Then send the token to create the account.
+ $newParams['token'] = $result['createaccount']['token'];
+ $request = new SimpleRequest( 'createaccount', $newParams );
+ $result = $this->api->postRequest( $request );
+ }
+ return ( $result['createaccount']['result'] === 'Success' );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php
new file mode 100644
index 00000000..d3bfbd5b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserGetter.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\User;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserGetter extends Service {
+
+ /**
+ * @param string $username
+ *
+ * @return User
+ */
+ public function getFromUsername( $username ) {
+ $result = $this->api->getRequest(
+ new SimpleRequest(
+ 'query', [
+ 'list' => 'users',
+ 'ususers' => $username,
+ 'usprop' => 'gender|emailable|registration|editcount|rights|implicitgroups|groups|blockinfo',
+ ]
+ )
+ );
+
+ return $this->newUserFromListUsersResult( array_shift( $result['query']['users'] ) );
+ }
+
+ /**
+ * @param array $array
+ *
+ * @return User
+ */
+ private function newUserFromListUsersResult( $array ) {
+ if ( array_key_exists( 'userid', $array ) ) {
+ return new User(
+ $array['name'],
+ $array['userid'],
+ $array['editcount'],
+ $array['registration'],
+ [ 'groups' => $array['groups'], 'implicitgroups' => $array['implicitgroups'] ],
+ $array['rights'],
+ $array['gender']
+ );
+ } else {
+ return new User(
+ $array['name'],
+ 0,
+ 0,
+ '',
+ [ 'groups' => [], 'implicitgroups' => [] ],
+ [],
+ ''
+ );
+ }
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php
new file mode 100644
index 00000000..4fbea9c1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/src/Service/UserRightsChanger.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Mediawiki\Api\Service;
+
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\User;
+
+/**
+ * @access private
+ *
+ * @author Addshore
+ */
+class UserRightsChanger extends Service {
+
+ /**
+ * @since 0.3
+ *
+ * @param User $user
+ * @param string[] $add
+ * @param string[] $remove
+ * @param array $extraParams
+ *
+ * @return bool
+ */
+ public function change(
+ User $user,
+ $add = [],
+ $remove = [],
+ array $extraParams = []
+ ) {
+ $result = $this->api->postRequest(
+ new SimpleRequest(
+ 'query', [
+ 'list' => 'users',
+ 'ustoken' => 'userrights',
+ 'ususers' => $user->getName(),
+ ]
+ )
+ );
+
+ $params = [
+ 'user' => $user->getName(),
+ 'token' => $result['query']['users'][0]['userrightstoken'],
+ ];
+ if ( !empty( $add ) ) {
+ $params['add'] = implode( '|', $add );
+ }
+ if ( !empty( $remove ) ) {
+ $params['remove'] = implode( '|', $remove );
+ }
+
+ $this->api->postRequest(
+ new SimpleRequest( 'userrights', array_merge( $extraParams, $params ) )
+ );
+
+ return true;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ.png b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ.png
new file mode 100644
index 00000000..9f8efbc8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/blue ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ.png
Binary files differ
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json
new file mode 100644
index 00000000..2933692a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/fixtures/namespaces.json
@@ -0,0 +1,241 @@
+{
+ "batchcomplete": "",
+ "query": {
+ "namespaces": {
+ "-2": {
+ "id": -2,
+ "case": "first-letter",
+ "canonical": "Media",
+ "*": "Medium"
+ },
+ "-1": {
+ "id": -1,
+ "case": "first-letter",
+ "canonical": "Special",
+ "*": "Spezial"
+ },
+ "0": {
+ "id": 0,
+ "case": "first-letter",
+ "content": "",
+ "*": ""
+ },
+ "1": {
+ "id": 1,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Talk",
+ "*": "Diskussion"
+ },
+ "2": {
+ "id": 2,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "User",
+ "*": "Benutzer"
+ },
+ "3": {
+ "id": 3,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "User talk",
+ "*": "Benutzer Diskussion"
+ },
+ "4": {
+ "id": 4,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Project",
+ "*": "Wikipedia"
+ },
+ "5": {
+ "id": 5,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Project talk",
+ "*": "Wikipedia Diskussion"
+ },
+ "6": {
+ "id": 6,
+ "case": "first-letter",
+ "canonical": "File",
+ "*": "Datei"
+ },
+ "7": {
+ "id": 7,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "File talk",
+ "*": "Datei Diskussion"
+ },
+ "8": {
+ "id": 8,
+ "case": "first-letter",
+ "canonical": "MediaWiki",
+ "*": "MediaWiki"
+ },
+ "9": {
+ "id": 9,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "MediaWiki talk",
+ "*": "MediaWiki Diskussion"
+ },
+ "10": {
+ "id": 10,
+ "case": "first-letter",
+ "canonical": "Template",
+ "*": "Vorlage"
+ },
+ "11": {
+ "id": 11,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Template talk",
+ "*": "Vorlage Diskussion"
+ },
+ "12": {
+ "id": 12,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Help",
+ "*": "Hilfe"
+ },
+ "13": {
+ "id": 13,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Help talk",
+ "*": "Hilfe Diskussion"
+ },
+ "14": {
+ "id": 14,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Category",
+ "*": "Kategorie"
+ },
+ "15": {
+ "id": 15,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Category talk",
+ "*": "Kategorie Diskussion"
+ },
+ "100": {
+ "id": 100,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Portal",
+ "*": "Portal"
+ },
+ "101": {
+ "id": 101,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Portal Diskussion",
+ "*": "Portal Diskussion"
+ },
+ "828": {
+ "id": 828,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Module",
+ "*": "Modul"
+ },
+ "829": {
+ "id": 829,
+ "case": "first-letter",
+ "subpages": "",
+ "canonical": "Module talk",
+ "*": "Modul Diskussion"
+ },
+ "2300": {
+ "id": 2300,
+ "case": "first-letter",
+ "canonical": "Gadget",
+ "*": "Gadget"
+ },
+ "2301": {
+ "id": 2301,
+ "case": "first-letter",
+ "canonical": "Gadget talk",
+ "*": "Gadget Diskussion"
+ },
+ "2302": {
+ "id": 2302,
+ "case": "case-sensitive",
+ "canonical": "Gadget definition",
+ "defaultcontentmodel": "GadgetDefinition",
+ "*": "Gadget-Definition"
+ },
+ "2303": {
+ "id": 2303,
+ "case": "case-sensitive",
+ "canonical": "Gadget definition talk",
+ "*": "Gadget-Definition Diskussion"
+ },
+ "2600": {
+ "id": 2600,
+ "case": "first-letter",
+ "canonical": "Topic",
+ "defaultcontentmodel": "flow-board",
+ "*": "Thema"
+ }
+ },
+ "namespacealiases": [
+ {
+ "id": 2,
+ "*": "Benutzerin"
+ },
+ {
+ "id": 3,
+ "*": "BD"
+ },
+ {
+ "id": 3,
+ "*": "Benutzerin Diskussion"
+ },
+ {
+ "id": 4,
+ "*": "WP"
+ },
+ {
+ "id": 5,
+ "*": "WD"
+ },
+ {
+ "id": 6,
+ "*": "Bild"
+ },
+ {
+ "id": 6,
+ "*": "Image"
+ },
+ {
+ "id": 7,
+ "*": "Bild Diskussion"
+ },
+ {
+ "id": 7,
+ "*": "Image talk"
+ },
+ {
+ "id": 12,
+ "*": "H"
+ },
+ {
+ "id": 13,
+ "*": "HD"
+ },
+ {
+ "id": 100,
+ "*": "P"
+ },
+ {
+ "id": 101,
+ "*": "PD"
+ }
+ ]
+ }
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php
new file mode 100644
index 00000000..7161447d
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/CategoryTraverserTest.php
@@ -0,0 +1,203 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\CategoryLoopException;
+use Mediawiki\Api\Service\CategoryTraverser;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Content;
+
+class CategoryTraverserTest extends \PHPUnit_Framework_TestCase {
+
+ /** @var TestEnvironment */
+ protected $testEnvironment;
+
+ /** @var \Mediawiki\Api\MediawikiFactory */
+ protected $factory;
+
+ /** @var \Mediawiki\Api\Service\CategoryTraverser */
+ protected $traverser;
+
+ public function setUp() {
+ parent::setUp();
+ $this->testEnvironment = TestEnvironment::newDefault();
+ $this->factory = $this->testEnvironment->getFactory();
+ $this->traverser = $this->factory->newCategoryTraverser();
+ }
+
+ /**
+ * A convenience wrapper around a PageDeleter.
+ * @param string[] $titles The titles to delete.
+ */
+ public function deletePages( $titles ) {
+ $deleter = $this->factory->newPageDeleter();
+ foreach ( $titles as $t ) {
+ // @todo Properly delete?
+ // $deleter->deleteFromPageTitle( new Title( $t ) );
+ $this->savePage( $t, '' );
+ }
+ }
+
+ /**
+ * A convenience wrapper to a RevisionSaver.
+ * @param string $title The title of the new page.
+ * @param string $content The wikitext to save to the page.
+ * @return Page The saved Page.
+ */
+ protected function savePage( $title, $content ) {
+ $pageIdentifier = new PageIdentifier( new Title( $title ) );
+ $revision = new Revision( new Content( $content ), $pageIdentifier );
+ $this->factory->newRevisionSaver()->save( $revision );
+ return $this->factory->newPageGetter()->getFromPageIdentifier( $pageIdentifier );
+ }
+
+ /**
+ * Get a list of all pages in a category or any of its descendants.
+ */
+ public function testDescendants() {
+ $rootCat = $this->savePage( 'Category:Root category', '' );
+ $this->savePage( 'Category:Sub category B', '[[Category:Root category]]' );
+ $this->savePage( 'Category:Sub category C', '[[Category:Root category]]' );
+ $this->savePage( 'Test page A1', 'Testing. [[Category:Root category]]' );
+ $this->savePage( 'Test page B1', 'Testing. [[Category:Sub category B]]' );
+ $this->savePage( 'Test page B2', 'Testing. [[Category:Sub category B]]' );
+ $this->savePage( 'Test page C1', 'Testing. [[Category:Sub category C]]' );
+ $this->testEnvironment->runJobs();
+
+ $callback = function ( Page $pageInfo, Page $parentCat ) {
+ $parentCatName = $parentCat->getPageIdentifier()->getTitle()->getText();
+ $thisPageName = $pageInfo->getPageIdentifier()->getTitle()->getText();
+ if ( $parentCatName === 'Category:Root category' ) {
+ $this->assertEquals( 'Test page A1', $thisPageName );
+ }
+ if ( $parentCatName === 'Category:Sub category C' ) {
+ $this->assertEquals( 'Test page C1', $thisPageName );
+ }
+ };
+ $this->traverser->addCallback( CategoryTraverser::CALLBACK_PAGE, $callback );
+ $decendants = $this->traverser->descend( $rootCat );
+ $this->assertCount( 4, $decendants->toArray() );
+ $this->deletePages( [
+ 'Category:Root category',
+ 'Category:Sub category B',
+ 'Category:Sub category C',
+ 'Test page A1',
+ 'Test page B1',
+ 'Test page B2',
+ 'Test page C1',
+ ] );
+ }
+
+ /**
+ * Make sure there aren't duplicate results when there are multiple paths to
+ * the same page.
+ */
+ public function testDescendantsWithMultiplePaths() {
+ $grandparent = $this->savePage( 'Category:Grandparent', '' );
+ $this->savePage( 'Category:Parent 1', '[[Category:Grandparent]]' );
+ $this->savePage( 'Category:Parent 2', '[[Category:Grandparent]]' );
+ $this->savePage( 'Parent 1', '[[Category:Grandparent]]' );
+ $this->savePage( 'Child 1', '[[Category:Parent 1]]' );
+ $this->savePage( 'Child 2', '[[Category:Parent 1]]' );
+ $this->savePage( 'Child 3', '[[Category:Parent 2]]' );
+ $this->testEnvironment->runJobs();
+ $decendants = $this->traverser->descend( $grandparent );
+ $this->assertCount( 4, $decendants->toArray() );
+ $this->deletePages( [
+ 'Category:Grandparent',
+ 'Category:Parent 1',
+ 'Category:Parent 2',
+ 'Child 1',
+ 'Child 2',
+ 'Child 3',
+ ] );
+ }
+
+ /**
+ * Categories should only be traversed once. For example, in the following graph, 'C' can be
+ * reached as a child of 'A' or of 'B', but only the first arrival will proceed to 'D':
+ *
+ * A
+ * | \
+ * | B
+ * | /
+ * C
+ * |
+ * D
+ *
+ */
+ public function testDescendantsOnlyVisitCatsOnce() {
+ global $wgVisitedCats;
+ $wgVisitedCats = [];
+ $catA = $this->savePage( 'Category:A cat', '' );
+ $this->savePage( 'Category:B cat', 'Testing. [[Category:A cat]]' );
+ $this->savePage( 'Category:C cat', 'Testing. [[Category:A cat]][[Category:B cat]]' );
+ $this->savePage( 'Category:D cat', 'Testing. [[Category:C cat]]' );
+ $this->testEnvironment->runJobs();
+ $callback = function ( Page $pageInfo, Page $parentCat ) {
+ global $wgVisitedCats;
+ $wgVisitedCats[] = $parentCat->getPageIdentifier()->getTitle()->getText();
+ };
+ $this->traverser->addCallback( CategoryTraverser::CALLBACK_CATEGORY, $callback );
+ $descendants = $this->traverser->descend( $catA );
+ $this->assertCount( 0, $descendants->toArray() );
+ $this->assertCount( 3, $wgVisitedCats );
+ $this->deletePages( [
+ 'Category:A cat',
+ 'Category:B cat',
+ 'Category:C cat',
+ 'Category:D cat',
+ ] );
+ }
+
+ /**
+ * Category loops are caught on descent.
+ *
+ * E
+ * / \
+ * F G
+ * / \
+ * H I
+ * |
+ * E <-- throw an Exception when we get to this repetition
+ *
+ */
+ public function testDescendIntoLoop() {
+ $catA = $this->savePage( 'Category:E cat', '[[Category:H cat]]' );
+ $catB = $this->savePage( 'Category:F cat', '[[Category:E cat]]' );
+ $catC = $this->savePage( 'Category:G cat', '[[Category:E cat]]' );
+ $catD = $this->savePage( 'Category:H cat', '[[Category:F cat]]' );
+ $catE = $this->savePage( 'Category:I cat', '[[Category:F cat]]' );
+ $this->testEnvironment->runJobs();
+ $haveCaught = false;
+ try {
+ $this->traverser->descend( $catA );
+ } catch ( CategoryLoopException $ex ) {
+ $haveCaught = true;
+ $expectedCatLoop = [
+ 'Category:E cat',
+ 'Category:F cat',
+ 'Category:H cat',
+ ];
+ // Build a simplified representation of the thrown loop pages, to get around different
+ // revision IDs.
+ $actualCatLoop = [];
+ foreach ( $ex->getCategoryPath()->toArray() as $p ) {
+ $actualCatLoop[] = $p->getPageIdentifier()->getTitle()->getText();
+ }
+ $this->assertEquals( $expectedCatLoop, $actualCatLoop );
+ }
+ $this->assertTrue( $haveCaught );
+ $this->deletePages( [
+ 'Category:E cat',
+ 'Category:F cat',
+ 'Category:G cat',
+ 'Category:H cat',
+ 'Category:I cat',
+ ] );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php
new file mode 100755
index 00000000..e805ee1d
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/FileUploaderTest.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\ApiUser;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\MediawikiFactory;
+use Mediawiki\Api\Service\FileUploader;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * Test the \Mediawiki\Api\Service\FileUploader class.
+ */
+class FileUploaderTest extends PHPUnit_Framework_TestCase {
+
+ /** @var MediawikiFactory */
+ protected $factory;
+
+ /** @var FileUploader */
+ protected $fileUploader;
+
+ /**
+ * Create a FileUploader to use in all these tests.
+ */
+ public function setup() {
+ parent::setup();
+ $testEnvironment = TestEnvironment::newDefault();
+ $this->factory = $testEnvironment->getFactory();
+ $this->fileUploader = $this->factory->newFileUploader();
+
+ // Log in as the sysop user. These credentials are referenced in docs/contributing.rst.
+ $localApiUser = new ApiUser( 'admin', 'admin123' );
+ $api = $testEnvironment->getApi();
+ $api->login( $localApiUser );
+ }
+
+ public function testUpload() {
+ $testPagename = uniqid( 'file-uploader-test-' ) . '.png';
+ $testTitle = new Title( 'File:'.$testPagename );
+
+ // Check that the file doesn't exist yet.
+ $testFile = $this->factory->newPageGetter()->getFromTitle( $testTitle );
+ $this->assertEquals( 0, $testFile->getPageIdentifier()->getId() );
+
+ // Upload a file.
+ $testFilename = dirname( __DIR__ ) . '/fixtures/blue ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ.png';
+ $uploaded = $this->fileUploader->upload( $testPagename, $testFilename, 'Testing',
+ null, null, true );
+ $this->assertTrue( $uploaded );
+
+ // Get the file again, and check that it exists this time.
+ $testFile2 = $this->factory->newPageGetter()->getFromTitle( $testTitle );
+ $this->assertGreaterThan( 0, $testFile2->getPageIdentifier()->getId() );
+ }
+
+ public function testUploadByChunks() {
+ $testPagename = uniqid( 'file-uploader-test-' ) . '.png';
+ $testTitle = new Title( 'File:'.$testPagename );
+
+ // Upload a 83725 byte file in 10k chunks.
+ $testFilename = dirname( __DIR__ ) . '/fixtures/blue ℳ𝒲♥𝓊𝓃𝒾𝒸ℴ𝒹ℯ.png';
+ $this->fileUploader->setChunkSize( 1024 * 10 );
+ $uploaded = $this->fileUploader->upload( $testPagename, $testFilename, 'Testing',
+ null, null, true );
+ $this->assertTrue( $uploaded );
+
+ // Get the file again, and check that it exists this time.
+ $testFile2 = $this->factory->newPageGetter()->getFromTitle( $testTitle );
+ $this->assertGreaterThan( 0, $testFile2->getPageIdentifier()->getId() );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php
new file mode 100644
index 00000000..eea55f9f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/NamespaceGetterTest.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\Service\NamespaceGetter;
+use Mediawiki\Api\SimpleRequest;
+use Mediawiki\DataModel\NamespaceInfo;
+
+class NamespaceGetterTest extends \PHPUnit_Framework_TestCase {
+ public function testGetNamespaceByCanonicalNameReturnsNullIfNamespaceWasNotFound() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $this->assertNull( $nsGetter->getNamespaceByCanonicalName( 'Dummy' ) );
+ }
+
+ public function testGetNamespaceByCanonicalNameReturnsNamespaceIfNamespaceWasFound() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $expectedNamespace = new NamespaceInfo( 1, 'Talk', 'Diskussion', 'first-letter' );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByCanonicalName( 'Talk' ) );
+ }
+
+ public function testGetNamespaceByNameTriesAllNames() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $expectedNamespace = new NamespaceInfo( 1, 'Talk', 'Diskussion', 'first-letter' );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName( 'Talk' ) );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName( 'Diskussion' ) );
+ }
+
+ public function testGetNamespaceByNameTriesAliases() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $expectedNamespace = new NamespaceInfo(
+ 3,
+ 'User talk',
+ 'Benutzer Diskussion',
+ 'first-letter',
+ null,
+ [ 'BD', 'Benutzerin Diskussion' ]
+ );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName(
+ 'Benutzerin Diskussion'
+ ) );
+ $this->assertEquals( $expectedNamespace, $nsGetter->getNamespaceByName( 'BD' ) );
+ }
+
+ public function testGetNamespacesReturnsAllNamespaces() {
+ $nsGetter = new NamespaceGetter( $this->getApi() );
+ $talkNamespace = new NamespaceInfo( 1, 'Talk', 'Diskussion', 'first-letter' );
+ $gadgetNamespace = new NamespaceInfo(
+ 2302,
+ 'Gadget definition',
+ 'Gadget-Definition',
+ 'case-sensitive',
+ 'GadgetDefinition'
+ );
+ $namespaces = $nsGetter->getNamespaces();
+ $this->assertCount( 27, $namespaces );
+ $this->assertArrayHasKey( 1, $namespaces );
+ $this->assertEquals( $talkNamespace, $namespaces[1] );
+ $this->assertArrayHasKey( 2302, $namespaces );
+ $this->assertEquals( $gadgetNamespace, $namespaces[2302] );
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject|MediawikiApi
+ */
+ private function getApi() {
+ $api = $this->getMockBuilder( MediawikiApi::class )->disableOriginalConstructor()->getMock();
+ $api->expects( $this->any() )
+ ->method( 'getRequest' )
+ ->with( $this->getRequest() )
+ ->willReturn( $this->getNamespaceFixture() );
+ return $api;
+ }
+
+ private function getRequest() {
+ return new SimpleRequest(
+ 'query', [
+ 'meta' => 'siteinfo',
+ 'siprop' => 'namespaces|namespacealiases'
+ ] );
+ }
+
+ private function getNamespaceFixture() {
+ return json_decode( file_get_contents( __DIR__ . '/../fixtures/namespaces.json' ), true );
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php
new file mode 100644
index 00000000..673eac8f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageIntegrationTest.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\DataModel\Content;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ */
+class PageIntegrationTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @var PageIdentifier
+ */
+ private static $localPageIdentifier;
+
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+ $title = new Title( 'TestPage - ' . strval( time() ) );
+ self::$localPageIdentifier = new PageIdentifier( $title );
+ }
+
+ public function testCreatePage() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $this->assertTrue(
+ $factory->newRevisionSaver()->save(
+ new Revision(
+ new Content( 'testCreatePage_content' ),
+ self::$localPageIdentifier
+ )
+ ),
+ 'Failed to Create Page ' . self::$localPageIdentifier->getTitle()->getText()
+ );
+ }
+
+ /**
+ * This is testGetPageUsingTitle as currently we only know the title
+ * @depends testCreatePage
+ */
+ public function testGetPageUsingTitle() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $page = $factory->newPageGetter()->getFromPageIdentifier( self::$localPageIdentifier );
+ $this->assertTrue( is_int( $page->getPageIdentifier()->getId() ) );
+ $title = $page->getPageIdentifier()->getTitle();
+ $this->assertEquals( self::$localPageIdentifier->getTitle(), $title );
+ $content = $page->getRevisions()->getLatest()->getContent()->getData();
+ $this->assertEquals( 'testCreatePage_content', $content );
+ self::$localPageIdentifier = $page->getPageIdentifier();
+ }
+
+ /**
+ * @depends testGetPageUsingTitle
+ */
+ public function testGetPageUsingId() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $page = $factory->newPageGetter()->getFromPageId( self::$localPageIdentifier->getId() );
+ $this->assertEquals( self::$localPageIdentifier->getId(), $page->getPageIdentifier()->getId() );
+ $title = $page->getPageIdentifier()->getTitle();
+ $this->assertEquals( self::$localPageIdentifier->getTitle(), $title );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php
new file mode 100644
index 00000000..39a973a4
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/PageListGetterTest.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\DataModel\Content;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * Test the \Mediawiki\Api\Service\PageListGetter class.
+ */
+class PageListGetterTest extends PHPUnit_Framework_TestCase {
+
+ /** @var string */
+ private $emptyCatName = 'Category:Empty category';
+
+ /** @var string */
+ private $nonemptyCatName = 'Category:Test category';
+
+ /** @var \Mediawiki\Api\Service\PageListGetter */
+ private $pageListGetter;
+
+ /**
+ * Set up some test categories and pages.
+ */
+ public function setUp() {
+ $testEnvironment = TestEnvironment::newDefault();
+ $factory = $testEnvironment->getFactory();
+
+ // An empty category.
+ $emptyCat = new PageIdentifier( new Title( $this->emptyCatName ) );
+ $factory->newRevisionSaver()->save( new Revision( new Content( '' ), $emptyCat ) );
+
+ // A non-empty category.
+ $testCat = new PageIdentifier( new Title( $this->nonemptyCatName ) );
+ $factory->newRevisionSaver()->save( new Revision( new Content( '' ), $testCat ) );
+
+ // Some pages in the latter.
+ // (Count must exceed the default categorymember result set size of 10.)
+ $revisionSaver = $factory->newRevisionSaver();
+ for ( $i = 1; $i <= 35; $i++ ) {
+ $testCat = new PageIdentifier( new Title( "Test page $i" ) );
+ // Even pages link to Main Page, odd pages transclude {{test}}.
+ $mainPageLink = ( ( $i % 2 ) == 0 ) ? 'Go to [[Main Page]].' : 'This is a {{test}}.';
+ $content = new Content( "$mainPageLink\n\n[[$this->nonemptyCatName]]" );
+ $revisionSaver->save( new Revision( $content, $testCat ) );
+ }
+
+ // Run all jobs, to make sure everything is up to date.
+ $testEnvironment->runJobs();
+
+ $this->pageListGetter = $factory->newPageListGetter();
+ }
+
+ public function testGetPageListFromCategoryName() {
+ // The empty category.
+ $emptyCategory = $this->pageListGetter->getPageListFromCategoryName( $this->emptyCatName );
+ $this->assertCount( 0, $emptyCategory->toArray() );
+
+ // The nonempty category.
+ $testCategory = $this->pageListGetter->getPageListFromCategoryName( $this->nonemptyCatName );
+ $this->assertCount( 35, $testCategory->toArray() );
+ }
+
+ public function testGetPageListFromPageTransclusions() {
+ $linksHere = $this->pageListGetter->getPageListFromPageTransclusions( 'Template:Test' );
+ // Only odd-numbered test pages link to the 'Test' template.
+ $this->assertCount( 18, $linksHere->toArray() );
+ }
+
+ public function testGetFromWhatLinksHere() {
+ // Every even-numbered test page links to Main Page.
+ $mainPageLinks = $this->pageListGetter->getFromWhatLinksHere( 'Main Page' );
+ $this->assertCount( 17, $mainPageLinks->toArray() );
+
+ // Nothing links to 'Test page 4'.
+ $testPageLinks = $this->pageListGetter->getFromWhatLinksHere( 'Test page 4' );
+ $this->assertCount( 0, $testPageLinks->toArray() );
+ }
+
+ public function testGetFromPrefix() {
+ // Pages with this prefix should be test pages 1, & 10-15; i.e. 7 of them.
+ $testPages = $this->pageListGetter->getFromPrefix( 'Test page 1' );
+ $this->assertCount( 11, $testPages->toArray() );
+ }
+
+ public function testGetRandom() {
+ // Default is 1.
+ $randomPages1 = $this->pageListGetter->getRandom();
+ $this->assertCount( 1, $randomPages1->toArray() );
+
+ // 8 random pages.
+ $randomPages2 = $this->pageListGetter->getRandom( [ 'rnlimit' => 8 ] );
+ $this->assertCount( 8, $randomPages2->toArray() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php
new file mode 100644
index 00000000..f40c0cd9
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/TestEnvironment.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\Guzzle\ClientFactory;
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\MediawikiFactory;
+use Mediawiki\Api\SimpleRequest;
+use Monolog\Handler\StreamHandler;
+use Monolog\Logger;
+
+/**
+ * @author Addshore
+ */
+class TestEnvironment {
+
+ /** @var \Mediawiki\Api\MediawikiFactory */
+ private $factory;
+
+ /** @var MediawikiApi */
+ protected $api;
+
+ /**
+ * Get a new default test environment.
+ * @return TestEnvironment
+ */
+ public static function newDefault() {
+ return new self();
+ }
+
+ /**
+ * Set up the test environment by creating a new API object pointing to a
+ * MediaWiki installation on localhost (or elsewhere as specified by the
+ * MEDIAWIKI_API_URL environment variable).
+ */
+ public function __construct() {
+ $this->factory = new MediawikiFactory( $this->getApi() );
+ }
+
+ /**
+ * Get the MediawikiApi to test against, based on the MEDIAWIKI_API_URL environment variable.
+ * @return MediawikiApi
+ * @throws \Exception If the MEDIAWIKI_API_URL environment variable does not end in 'api.php'
+ */
+ public function getApi() {
+ if ( $this->api instanceof MediawikiApi ) {
+ return $this->api;
+ }
+ $apiUrl = getenv( 'MEDIAWIKI_API_URL' );
+ if ( empty( $apiUrl ) ) {
+ $apiUrl = 'http://localhost/w/api.php';
+ } elseif ( substr( $apiUrl, -7 ) !== 'api.php' ) {
+ $msg = "URL incorrect: $apiUrl"
+ . " (the MEDIAWIKI_API_URL environment variable should end in 'api.php')";
+ throw new \Exception( $msg );
+ }
+
+ // Log to a local file.
+ $logger = new Logger( 'mediawiki-api' );
+ $logFile = __DIR__ . '/../../log/mediawiki-api.log';
+ $logger->pushHandler( new StreamHandler( $logFile, Logger::DEBUG ) );
+
+ // Create and return the API object.
+ $this->api = new MediawikiApi( $apiUrl );
+ $this->api->setLogger( $logger );
+ return $this->api;
+ }
+
+ /**
+ * Get the MediaWiki factory.
+ *
+ * @return \Mediawiki\Api\MediawikiFactory The factory instance.
+ */
+ public function getFactory() {
+ return $this->factory;
+ }
+
+ /**
+ * Run all jobs in the queue. This only works if the MediaWiki installation has $wgJobRunRate
+ * set to greater than zero (for test-running, you should set it to something higher than 50).
+ * @todo This and TestEnvironment::getJobQueueLength() should probably not live here.
+ * @return void
+ */
+ public function runJobs() {
+ $reqestProps = [ 'meta' => 'siteinfo', 'siprop' => 'general' ];
+ $siteInfoRequest = new SimpleRequest( 'query', $reqestProps );
+ $out = $this->getApi()->getRequest( $siteInfoRequest );
+ $mainPageUrl = $out['query']['general']['base'];
+ $i = 0;
+ while ( $this->getJobQueueLength( $this->getApi() ) > 0 ) {
+ $i++;
+ $cf = new ClientFactory();
+ $cf->getClient()->get( $mainPageUrl );
+ if ($i == 10) {
+ // Give up if we've been looping too much. This is very arbitrary.
+ break;
+ }
+ }
+ }
+
+ /**
+ * Get the number of jobs currently in the queue.
+ * @todo This and TestEnvironment::runJobs() should probably not live here.
+ * @param MediawikiApi $api
+ * @return int
+ */
+ public function getJobQueueLength( MediawikiApi $api ) {
+ $req = new SimpleRequest( 'query', [
+ 'meta' => 'siteinfo',
+ 'siprop' => 'statistics',
+ ]
+ );
+ $out = $api->getRequest( $req );
+ return (int)$out['query']['statistics']['jobs'];
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php
new file mode 100644
index 00000000..29221edb
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/UserIntegrationTest.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\ApiUser;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @author Addshore
+ */
+class UserIntegrationTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @var ApiUser
+ */
+ private static $localApiUser;
+
+ public static function setUpBeforeClass() {
+ parent::setUpBeforeClass();
+ $strTime = strval( time() );
+ self::$localApiUser = new ApiUser( 'TestUser - ' . strval( time() ), $strTime . '-pass' );
+ }
+
+ public function testCreateUser() {
+ $factory = TestEnvironment::newDefault()->getFactory();
+ $createResult = $factory->newUserCreator()->create(
+ self::$localApiUser->getUsername(),
+
+
+ self::$localApiUser->getPassword()
+ );
+ $this->assertTrue( $createResult );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist
new file mode 100644
index 00000000..4a0040c3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/integration/phpunit.xml.dist
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- convertWarningsToExceptions is false as real API calls can return un expected warnings -->
+<phpunit
+ bootstrap="../../vendor/autoload.php"
+ colors="true"
+ convertWarningsToExceptions="false"
+ >
+ <testsuites>
+ <testsuite name="addwiki/mediawiki-api/integration">
+ <directory suffix="Test.php">.</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./../../src</directory>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php
new file mode 100644
index 00000000..67d6e450
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/AnonymousGeneratorTest.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Mediawiki\Api\Test\Generator;
+
+use Mediawiki\Api\Generator\AnonymousGenerator;
+
+/**
+ * @author Addshore
+ *
+ * @covers \Mediawiki\Api\Generator\AnonymousGenerator
+ */
+class AnonymousGeneratorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testConstruction() {
+ $generator = new AnonymousGenerator( 'name', [ 'gfoo' => 'bar' ] );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ ],
+ $generator->getParams()
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php
new file mode 100644
index 00000000..bd7cb0bf
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Generator/FluentGeneratorTest.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Mediawiki\Api\Test\Generator;
+
+use Mediawiki\Api\Generator\FluentGenerator;
+
+/**
+ * @author Addshore
+ *
+ * @covers \Mediawiki\Api\Generator\FluentGenerator
+ */
+class FluentGeneratorTest extends \PHPUnit_Framework_TestCase {
+
+ public function testConstructionWithNoGPrefix() {
+ $generator = new FluentGenerator( 'name' );
+ $generator->set( 'foo', 'bar' );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ ],
+ $generator->getParams()
+ );
+ }
+
+ public function testConstructionWithGPrefix() {
+ $generator = new FluentGenerator( 'name' );
+ $generator->set( 'gfoo', 'bar' );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ ],
+ $generator->getParams()
+ );
+ }
+
+ public function testFluidity() {
+ $generator = FluentGenerator::factory( 'name' )
+ ->set( 'foo', 'bar' )
+ ->set( 'gcat', 'meow' );
+
+ $this->assertEquals(
+ [
+ 'generator' => 'name',
+ 'gfoo' => 'bar',
+ 'gcat' => 'meow',
+ ],
+ $generator->getParams()
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php
new file mode 100644
index 00000000..c4ce958f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/MediawikiFactoryTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Mediawiki\Api\Test;
+
+use Mediawiki\Api\MediawikiFactory;
+
+/**
+ * @covers Mediawiki\Api\MediawikiFactory
+ *
+ * @author Addshore
+ */
+class MediawikiFactoryTest extends \PHPUnit_Framework_TestCase {
+
+ public function getMockMediawikiApi() {
+ return $this->getMockBuilder( 'Mediawiki\Api\MediawikiApi' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ public function provideFactoryMethodsTest() {
+ return [
+ [ 'Mediawiki\Api\Service\RevisionSaver', 'newRevisionSaver' ],
+ [ 'Mediawiki\Api\Service\RevisionUndoer', 'newRevisionUndoer' ],
+ [ 'Mediawiki\Api\Service\PageGetter', 'newPageGetter' ],
+ [ 'Mediawiki\Api\Service\UserGetter', 'newUserGetter' ],
+ [ 'Mediawiki\Api\Service\PageDeleter', 'newPageDeleter' ],
+ [ 'Mediawiki\Api\Service\PageMover', 'newPageMover' ],
+ [ 'Mediawiki\Api\Service\PageListGetter', 'newPageListGetter' ],
+ [ 'Mediawiki\Api\Service\PageRestorer', 'newPageRestorer' ],
+ [ 'Mediawiki\Api\Service\PagePurger', 'newPagePurger' ],
+ [ 'Mediawiki\Api\Service\RevisionRollbacker', 'newRevisionRollbacker' ],
+ [ 'Mediawiki\Api\Service\RevisionPatroller', 'newRevisionPatroller' ],
+ [ 'Mediawiki\Api\Service\PageProtector', 'newPageProtector' ],
+ [ 'Mediawiki\Api\Service\PageWatcher', 'newPageWatcher' ],
+ [ 'Mediawiki\Api\Service\RevisionDeleter', 'newRevisionDeleter' ],
+ [ 'Mediawiki\Api\Service\RevisionRestorer', 'newRevisionRestorer' ],
+ [ 'Mediawiki\Api\Service\UserBlocker', 'newUserBlocker' ],
+ [ 'Mediawiki\Api\Service\UserRightsChanger', 'newUserRightsChanger' ],
+ [ 'Mediawiki\Api\Service\UserCreator', 'newUserCreator' ],
+ [ 'Mediawiki\Api\Service\LogListGetter', 'newLogListGetter' ],
+ [ 'Mediawiki\Api\Service\FileUploader', 'newFileUploader' ],
+ [ 'Mediawiki\Api\Service\ImageRotator', 'newImageRotator' ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideFactoryMethodsTest
+ */
+ public function testFactoryMethod( $class, $method ) {
+ $factory = new MediawikiFactory( $this->getMockMediawikiApi() );
+ $this->assertInstanceOf( $class, $factory->$method() );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php
new file mode 100644
index 00000000..38199c26
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/Service/PagePurgerTest.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Mediawiki\Api\Test\Service;
+
+use Mediawiki\Api\MediawikiApi;
+use Mediawiki\Api\Service\PagePurger;
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\Pages;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+use PHPUnit_Framework_MockObject_MockObject;
+
+/**
+ * @author Addshore
+ * @covers Mediawiki\Api\Service\PagePurger
+ */
+class PagePurgerTest extends \PHPUnit_Framework_TestCase {
+
+ private function getMockApi() {
+ /** @var MediawikiApi|PHPUnit_Framework_MockObject_MockObject $mock */
+ $mock = $this->getMockBuilder( '\Mediawiki\Api\MediawikiApi' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ return $mock;
+ }
+
+ public function testValidConstruction() {
+ new PagePurger( $this->getMockApi() );
+ $this->assertTrue( true );
+ }
+
+ public function testPurgePage() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue(
+ [
+ "batchcomplete" => "",
+ "purge" => [ [ "ns" => 0, "title" => "Foo", "purged" => "" ] ]
+ ] ) );
+
+ $service = new PagePurger( $api );
+
+ $page = new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 123
+ )
+ );
+
+ $this->assertTrue( $service->purge( $page ) );
+ }
+
+ function testIncorrectPurgePage() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue( [
+ "batchcomplete" => "",
+ "purge" =>
+ [ [
+ "ns" => 0,
+ "title" => "This page really does not exist",
+ "missing" => ""
+ ] ]
+ ] ) );
+
+ $service = new PagePurger( $api );
+
+ $page = new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 123
+ )
+ );
+
+ $this->assertFalse( $service->purge( $page ) );
+ }
+
+ public function testPurgePages() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue(
+ [
+ "batchcomplete" => "",
+ "purge" => [
+ [
+ "ns" => 0,
+ "title" => "Foo",
+ "purged" => ""
+ ],
+ [
+ "ns" => 0,
+ "title" => "Bar",
+ "purged" => ""
+ ],
+ ]
+ ]
+ ) );
+
+ $service = new PagePurger( $api );
+
+ $pages = new Pages( [
+ new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 100
+ )
+ ), new Page(
+ new PageIdentifier(
+ new Title( 'Bar', 1 ),
+ 101
+ )
+ ) ] );
+
+ $this->assertEquals( $service->purgePages( $pages ), $pages );
+ }
+
+ function testIncorrectPurgePages() {
+ $api = $this->getMockApi();
+ $api->expects( $this->once() )
+ ->method( 'postRequest' )
+ ->with(
+ $this->isInstanceOf( '\Mediawiki\Api\SimpleRequest' )
+ )
+ ->will( $this->returnValue(
+ [
+ "batchcomplete" => "",
+ "purge" => [
+ [
+ "ns" => 0,
+ "title" => "Foo",
+ "purged" => ""
+ ],
+ [
+ "ns" => 0,
+ "title" => "Bar",
+ "purged" => ""
+ ],
+ [
+ "ns" => 0,
+ "title" => "This page really does not exist",
+ "missing" => ""
+ ],
+ ]
+ ]
+ ) );
+
+ $service = new PagePurger( $api );
+
+ $pages = new Pages( [
+ new Page(
+ new PageIdentifier(
+ new Title( 'Foo', 0 ),
+ 100
+ )
+ ), new Page(
+ new PageIdentifier(
+ new Title( 'Bar', 1 ),
+ 101
+ )
+ ), new Page(
+ new PageIdentifier(
+ new Title( 'MissingPage', 1 ),
+ 103
+ )
+ ) ] );
+
+ // MissingPage is not in the pages that are returned by purgePages
+ $pagesArray = $pages->toArray();
+ array_pop( $pagesArray );
+ $result = new Pages( $pagesArray );
+
+ $this->assertEquals( $service->purgePages( $pages ), $result );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist
new file mode 100644
index 00000000..8f4cecef
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-api/tests/unit/phpunit.xml.dist
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="../../vendor/autoload.php" colors="true">
+ <testsuites>
+ <testsuite name="addwiki/mediawiki-api/unit">
+ <directory suffix="Test.php">.</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">./../../src</directory>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/.gitignore b/bin/wiki/vendor/addwiki/mediawiki-datamodel/.gitignore
new file mode 100644
index 00000000..014936d3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/.gitignore
@@ -0,0 +1,5 @@
+.idea
+vendor
+composer.lock
+test.php
+docs/_build
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/.scrutinizer.yml b/bin/wiki/vendor/addwiki/mediawiki-datamodel/.scrutinizer.yml
new file mode 100644
index 00000000..ffc976e3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/.scrutinizer.yml
@@ -0,0 +1,13 @@
+inherit: true
+
+tools:
+ php_code_sniffer: true
+ php_cpd: true
+ php_cs_fixer: true
+ php_loc: true
+ php_mess_detector: true
+ php_pdepend: true
+ php_analyzer: true
+ sensiolabs_security_checker: true
+ external_code_coverage:
+ timeout: 300 \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/.travis.yml b/bin/wiki/vendor/addwiki/mediawiki-datamodel/.travis.yml
new file mode 100644
index 00000000..a58745e1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/.travis.yml
@@ -0,0 +1,25 @@
+language: php
+
+php:
+ - hhvm
+ - 5.5
+ - 5.6
+ - 7.0
+ - 7.1
+
+before_script:
+ - composer install
+
+script:
+ - ./vendor/bin/phpunit --coverage-clover=coverage.clover
+
+after_script:
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
+
+notifications:
+ irc:
+ channels:
+ - "chat.freenode.net##add"
+ on_success: change
+ on_failure: always
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/LICENSE.md b/bin/wiki/vendor/addwiki/mediawiki-datamodel/LICENSE.md
new file mode 100644
index 00000000..0671f06a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/LICENSE.md
@@ -0,0 +1,264 @@
+The GNU General Public License, Version 2, June 1991 (GPLv2)
+============================================================
+
+> Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+
+Preamble
+--------
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most
+of the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for this service if you wish),
+that you receive source code or can get it if you want it, that you can change
+the software or use pieces of it in new free programs; and that you know you can
+do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show them
+these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's free
+use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+
+Terms And Conditions For Copying, Distribution And Modification
+---------------------------------------------------------------
+
+**0.** This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program or
+work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or
+translated into another language. (Hereinafter, translation is included without
+limitation in the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+**1.** You may copy and distribute verbatim copies of the Program's source code
+as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the Program
+a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+**2.** You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+* **a)** You must cause the modified files to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+* **b)** You must cause any work that you distribute or publish, that in whole
+ or in part contains or is derived from the Program or any part thereof, to
+ be licensed as a whole at no charge to all third parties under the terms of
+ this License.
+
+* **c)** If the modified program normally reads commands interactively when
+ run, you must cause it, when started running for such interactive use in the
+ most ordinary way, to print or display an announcement including an
+ appropriate copyright notice and a notice that there is no warranty (or
+ else, saying that you provide a warranty) and that users may redistribute
+ the program under these conditions, and telling the user how to view a copy
+ of this License. (Exception: if the Program itself is interactive but does
+ not normally print such an announcement, your work based on the Program is
+ not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License, and
+its terms, do not apply to those sections when you distribute them as separate
+works. But when you distribute the same sections as part of a whole which is a
+work based on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the entire whole,
+and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your
+rights to work written entirely by you; rather, the intent is to exercise the
+right to control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+**3.** You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+* **a)** Accompany it with the complete corresponding machine-readable source
+ code, which must be distributed under the terms of Sections 1 and 2 above on
+ a medium customarily used for software interchange; or,
+
+* **b)** Accompany it with a written offer, valid for at least three years, to
+ give any third party, for a charge no more than your cost of physically
+ performing source distribution, a complete machine-readable copy of the
+ corresponding source code, to be distributed under the terms of Sections 1
+ and 2 above on a medium customarily used for software interchange; or,
+
+* **c)** Accompany it with the information you received as to the offer to
+ distribute corresponding source code. (This alternative is allowed only for
+ noncommercial distribution and only if you received the program in object
+ code or executable form with such an offer, in accord with Subsection b
+ above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface
+definition files, plus the scripts used to control compilation and installation
+of the executable. However, as a special exception, the source code distributed
+need not include anything that is normally distributed (in either source or
+binary form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component itself
+accompanies the executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source code
+from the same place counts as distribution of the source code, even though third
+parties are not compelled to copy the source along with the object code.
+
+**4.** You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so
+long as such parties remain in full compliance.
+
+**5.** You are not required to accept this License, since you have not signed
+it. However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you do
+not accept this License. Therefore, by modifying or distributing the Program (or
+any work based on the Program), you indicate your acceptance of this License to
+do so, and all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+**6.** Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+**7.** If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution of
+the Program by all those who receive copies directly or indirectly through you,
+then the only way you could satisfy both it and this License would be to refrain
+entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and the
+section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose that
+choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+**8.** If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+**9.** The Free Software Foundation may publish revised and/or new versions of
+the General Public License from time to time. Such new versions will be similar
+in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that version
+or of any later version published by the Free Software Foundation. If the
+Program does not specify a version number of this License, you may choose any
+version ever published by the Free Software Foundation.
+
+**10.** If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of
+all derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+
+No Warranty
+-----------
+
+**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
+THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
+INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
+BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
+OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/README.md b/bin/wiki/vendor/addwiki/mediawiki-datamodel/README.md
new file mode 100644
index 00000000..f0fdf1b1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/README.md
@@ -0,0 +1,17 @@
+mediawiki-datamodel
+==================
+[![Build Status](https://travis-ci.org/addwiki/mediawiki-datamodel.png?branch=master)](https://travis-ci.org/addwiki/mediawiki-datamodel)
+[![Code Coverage](https://scrutinizer-ci.com/g/addwiki/mediawiki-datamodel/badges/coverage.png?s=ce4091cc4471ee9feff0c5fd963101c93bf54080)](https://scrutinizer-ci.com/g/addwiki/mediawiki-datamodel/)
+[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/addwiki/mediawiki-datamodel/badges/quality-score.png?s=9383c67ac0068ac3052243cd636e05eafd505b80)](https://scrutinizer-ci.com/g/addwiki/mediawiki-datamodel/)
+
+On Packagist:
+[![Latest Stable Version](https://poser.pugx.org/addwiki/mediawiki-datamodel/version.png)](https://packagist.org/packages/addwiki/mediawiki-datamodel)
+[![Download count](https://poser.pugx.org/addwiki/mediawiki-datamodel/d/total.png)](https://packagist.org/packages/addwiki/mediawiki-datamodel)
+
+Issue tracker: https://phabricator.wikimedia.org/project/profile/1490/
+
+## Installation
+
+Use composer to install the library and all its dependencies:
+
+ composer require "addwiki/mediawiki-datamodel:~0.7.0"
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/RELEASENOTES.md b/bin/wiki/vendor/addwiki/mediawiki-datamodel/RELEASENOTES.md
new file mode 100644
index 00000000..08df4aab
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/RELEASENOTES.md
@@ -0,0 +1,88 @@
+These are the release notes for the [mediawiki-datamodel](README.md).
+
+## Version 0.7.1 (10th January 2017)
+
+* [T184567](https://phabricator.wikimedia.org/T184567) `User` objects can now be created with a `null` `$registration`.
+
+## Version 0.7 (8th March 2017)
+
+#### New features
+
+* Add NamespaceInfo class
+
+## Version 0.6 (2015-09-04)
+
+#### Compatibility changes
+
+* Log object now takes a PageIdentifier object instead of a Page object
+
+#### Deprecations
+
+* Title::getTitle is deprecated
+
+#### New features
+
+* Implemented File class
+* Implemented Redirect class
+* Title::getText introduced to replace getTitle
+* Log now implements JsonSerializable
+* LogList now implements JsonSerializable
+* Title now implements JsonSerializable
+* PageIdentifier now implements JsonSerializable
+
+## Version 0.5 (2015-01-13)
+
+#### Compatibility changes
+
+* Revision objects now require a PageIdentifier object instead of a $pageId int
+* Page objects now require a PageIdentifier objects instead of a $title and $pageId
+* Content getNativeData renamed to getData
+* Content constructor changed, now takes data and optional model
+* Content has new method getModel in places of random constants
+* Removed WikitextContent class. Content is no longer abstract.
+
+#### New features
+
+* Implemented Log class
+* Implemented LogList class
+* Introduce PageIdentifier class
+* Page objects can be constructed without a Revisions object
+
+## Version 0.4 (2014-07-08)
+
+* Page objects now ONLY accept a Title object for $title in their constructor.
+* InvalidArgumentExceptions are now thrown when objects are constructed with the wrong types.
+* User objects now split up implicitgroups and regular groups, thus $groups is now array[]
+
+
+## Version 0.3 (2014-06-24)
+
+#### Compatibility changes
+
+* Revision objects now take a Content object as $content
+
+#### Additions
+
+* Content class
+* WikitextContent class
+* Pages class
+
+
+## Version 0.2 (2014-02-23)
+
+#### Compatibility changes
+
+* Revision enhanced to allow more flexibility, Constructor and public functions have changed
+* contentmodel has been removed from the Page class
+
+
+## Version 0.1 (2014-02-23)
+
+Initial release with the following features:
+
+* EditInfo
+* Page
+* Revision
+* Revisions
+* Title
+* User
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/composer.json b/bin/wiki/vendor/addwiki/mediawiki-datamodel/composer.json
new file mode 100644
index 00000000..9ac4e519
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/composer.json
@@ -0,0 +1,32 @@
+{
+ "name": "addwiki/mediawiki-datamodel",
+ "type": "library",
+ "description": "A Mediawiki datamodel",
+ "keywords": ["Mediawiki"],
+ "license": "GPL-2.0+",
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\DataModel\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Mediawiki\\DataModel\\Test\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.0|~5.3.0",
+ "jakub-onderka/php-parallel-lint": "0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0"
+ }
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/Makefile b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/Makefile
new file mode 100644
index 00000000..af9b9d0b
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/Makefile
@@ -0,0 +1,225 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " epub3 to make an epub3"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+ @echo " dummy to check syntax errors of document sources"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mediawiki-datamodel.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mediawiki-datamodel.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/mediawiki-datamodel"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mediawiki-datamodel"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: epub3
+epub3:
+ $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+ @echo
+ @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+ $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+ @echo
+ @echo "Build finished. Dummy builder generates no files."
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/conf.py b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/conf.py
new file mode 100644
index 00000000..1f465d00
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/conf.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+import sys, os
+from sphinx.highlighting import lexers
+from pygments.lexers.web import PhpLexer
+
+lexers['php'] = PhpLexer(startinline=True, linenos=1)
+lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1)
+primary_domain = 'php'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'mediawiki-datamodel'
+copyright = '2016, addwiki'
+author = 'addwiki'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.6'
+# The full version, including alpha/beta/rc tags.
+release = '0.6'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'default'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'mediawiki-datamodeldoc'
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/index.rst b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/index.rst
new file mode 100644
index 00000000..c862502f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/index.rst
@@ -0,0 +1,22 @@
+.. mediawiki-datamodel documentation master file, created by
+ sphinx-quickstart on Sat Oct 1 18:15:20 2016.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to mediawiki-datamodel's documentation!
+===============================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/make.bat b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/make.bat
new file mode 100644
index 00000000..6ff153c5
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/docs/make.bat
@@ -0,0 +1,281 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^<target^>` where ^<target^> is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. epub3 to make an epub3
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ echo. dummy to check syntax errors of document sources
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\mediawiki-datamodel.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\mediawiki-datamodel.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "epub3" (
+ %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+if "%1" == "dummy" (
+ %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. Dummy builder generates no files.
+ goto end
+)
+
+:end
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/phpunit.xml.dist b/bin/wiki/vendor/addwiki/mediawiki-datamodel/phpunit.xml.dist
new file mode 100644
index 00000000..d8bbce08
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/phpunit.xml.dist
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit bootstrap="./vendor/autoload.php" colors="true">
+ <testsuites>
+ <testsuite name="addwiki/mediawiki-datamodel">
+ <directory suffix="Test.php">./tests</directory>
+ </testsuite>
+ </testsuites>
+ <filter>
+ <whitelist addUncoveredFilesFromWhitelist="true">
+ <directory suffix=".php">src</directory>
+ </whitelist>
+ </filter>
+</phpunit> \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Content.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Content.php
new file mode 100644
index 00000000..c1372d46
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Content.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use LogicException;
+
+/**
+ * Class Representing the content of a revision
+ * @author Addshore
+ */
+class Content {
+
+ /**
+ * @var string sha1 hash of the object content upon creation
+ */
+ private $initialHash;
+
+ /**
+ * @var mixed
+ */
+ private $data;
+
+ /**
+ * @var string|null
+ */
+ private $model;
+
+ /**
+ * Should always be called AFTER overriding constructors so a hash can be created
+ *
+ * @param mixed $data
+ * @param string|null $model
+ */
+ public function __construct( $data, $model = null ) {
+ $this->data = $data;
+ $this->model = $model;
+ $this->initialHash = $this->getHash();
+ }
+
+ /**
+ * @return string
+ */
+ public function getModel() {
+ return $this->model;
+ }
+
+ /**
+ * Returns a sha1 hash of the content
+ *
+ * @throws LogicException
+ * @return string
+ */
+ public function getHash() {
+ $data = $this->getData();
+ if( is_object( $data ) ) {
+ if( method_exists( $data, 'getHash' ) ) {
+ return $data->getHash();
+ } else {
+ return sha1( serialize( $data ) );
+ }
+ }
+ if( is_string( $data ) ) {
+ return sha1( $data );
+ }
+ throw new LogicException( "Cant get hash for data of type: " . gettype( $data ) );
+ }
+
+ /**
+ * Has the content been changed since object construction (this shouldn't happen!)
+ * @return bool
+ */
+ public function hasChanged() {
+ return $this->initialHash !== $this->getHash();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getData() {
+ return $this->data;
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/EditInfo.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/EditInfo.php
new file mode 100644
index 00000000..b6ba7845
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/EditInfo.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+
+/**
+ * Represents flags that can be used when edits are made
+ * @author Addshore
+ */
+class EditInfo {
+
+ //minor flags
+ const MINOR = true;
+ const NOTMINOR = false;
+ //bot flags
+ const BOT = true;
+ const NOTBOT = false;
+
+ /**
+ * @var EditInfo::MINOR|self::NOTMINOR
+ */
+ protected $minor = false;
+
+ /**
+ * @var EditInfo::BOT|self::NOTBOT
+ */
+ protected $bot = false;
+
+ /**
+ * @var string
+ */
+ protected $summary = null;
+
+ /**
+ * @param string $summary
+ * @param bool $minor
+ * @param bool $bot
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $summary = '', $minor = self::NOTMINOR, $bot = self::NOTBOT ) {
+ if( !is_string( $summary ) ) {
+ throw new InvalidArgumentException( '$summary must be a string' );
+ }
+ if( !is_bool( $minor ) ) {
+ throw new InvalidArgumentException( '$minor must be a bool' );
+ }
+ if( !is_bool( $bot ) ) {
+ throw new InvalidArgumentException( '$bot must be a bool' );
+ }
+
+ $this->summary = $summary;
+ $this->bot = $bot;
+ $this->minor = $minor;
+ }
+
+ /**
+ * @return EditInfo::BOT|self::NOTBOT
+ */
+ public function getBot() {
+ return $this->bot;
+ }
+
+ /**
+ * @return EditInfo::MINOR|self::NOTMINOR
+ */
+ public function getMinor() {
+ return $this->minor;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSummary() {
+ return $this->summary;
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/File.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/File.php
new file mode 100644
index 00000000..7851b3b8
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/File.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+
+/**
+ * @author Addshore
+ */
+class File extends Page {
+
+ /**
+ * @var string
+ */
+ private $url;
+
+ /**
+ * @param string $url
+ * @param PageIdentifier $pageIdentifier
+ * @param Revisions $revisions
+ */
+ public function __construct( $url, PageIdentifier $pageIdentifier = null, Revisions $revisions = null ) {
+ parent::__construct( $pageIdentifier, $revisions );
+ if( !is_string( $url ) ) {
+ throw new InvalidArgumentException( '$url must be a string' );
+ }
+ $this->url = $url;
+ }
+
+ /**
+ * @return string
+ */
+ public function getUrl() {
+ return $this->url;
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Log.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Log.php
new file mode 100644
index 00000000..d3e48637
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Log.php
@@ -0,0 +1,171 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use JsonSerializable;
+
+/**
+ * @since 0.5
+ */
+class Log implements JsonSerializable {
+
+ /**
+ * @var int
+ */
+ private $id;
+
+ /**
+ * @var string
+ */
+ private $type;
+
+ /**
+ * @var string
+ */
+ private $action;
+
+ /**
+ * @var string
+ */
+ private $timestamp;
+
+ /**
+ * @var string
+ */
+ private $user;
+
+ /**
+ * @var string
+ */
+ private $comment;
+
+ /**
+ * @var PageIdentifier
+ */
+ private $pageIdentifier;
+
+ /**
+ * @var array
+ */
+ private $details;
+
+ /**
+ * @param int $id
+ * @param string $type
+ * @param string $action
+ * @param string $timestamp
+ * @param string $user
+ * @param PageIdentifier $pageIdentifier
+ * @param string $comment
+ * @param array $details
+ */
+ public function __construct( $id, $type, $action, $timestamp, $user, $pageIdentifier, $comment, $details ) {
+ $this->id = $id;
+ $this->type = $type;
+ $this->action = $action;
+ $this->timestamp = $timestamp;
+ $this->user = $user;
+ $this->pageIdentifier = $pageIdentifier;
+ $this->comment = $comment;
+ $this->details = $details;
+ }
+
+ /**
+ * @since 0.5
+ * @return string
+ */
+ public function getUser() {
+ return $this->user;
+ }
+
+ /**
+ * @since 0.5
+ * @return string
+ */
+ public function getAction() {
+ return $this->action;
+ }
+
+ /**
+ * @since 0.5
+ * @return string
+ */
+ public function getComment() {
+ return $this->comment;
+ }
+
+ /**
+ * @since 0.5
+ * @return int
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * @since 0.6
+ * @return PageIdentifier
+ */
+ public function getPageIdentifier() {
+ return $this->pageIdentifier;
+ }
+
+ /**
+ * @since 0.5
+ * @return string
+ */
+ public function getTimestamp() {
+ return $this->timestamp;
+ }
+
+ /**
+ * @since 0.5
+ * @return string
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * @since 0.5
+ * @return array
+ */
+ public function getDetails() {
+ return $this->details;
+ }
+
+ /**
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ public function jsonSerialize() {
+ return array(
+ 'id' => $this->id,
+ 'type' => $this->type,
+ 'action' => $this->action,
+ 'timestamp' => $this->timestamp,
+ 'user' => $this->user,
+ 'pageidentifier' => $this->pageIdentifier,
+ 'comment' => $this->comment,
+ 'details' => $this->details,
+ );
+ }
+
+ /**
+ * @param array $json
+ *
+ * @return self
+ */
+ public static function jsonDeserialize( $json ) {
+ return new self(
+ $json['id'],
+ $json['type'],
+ $json['action'],
+ $json['timestamp'],
+ $json['user'],
+ PageIdentifier::jsonDeserialize( $json['pageidentifier'] ),
+ $json['comment'],
+ $json['details']
+ );
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/LogList.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/LogList.php
new file mode 100644
index 00000000..9ce30263
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/LogList.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+use JsonSerializable;
+use RuntimeException;
+
+/**
+ * Represents a collection of Log classes
+ * @author Addshore
+ */
+class LogList implements JsonSerializable {
+
+ /**
+ * @var Log[]
+ */
+ private $logs;
+
+ /**
+ * @param Log[] $logs
+ */
+ public function __construct( $logs = array() ) {
+ $this->logs = array();
+ $this->addLogs( $logs );
+ }
+
+ /**
+ * @param Log[]|LogList $logs
+ *
+ * @throws InvalidArgumentException
+ */
+ public function addLogs( $logs ) {
+ if( !is_array( $logs ) && !$logs instanceof LogList ) {
+ throw new InvalidArgumentException( '$logs needs to either be an array or a LogList object' );
+ }
+ if( $logs instanceof LogList ) {
+ $logs = $logs->toArray();
+ }
+ foreach( $logs as $log ) {
+ $this->addLog( $log );
+ }
+ }
+
+ /**
+ * @param Log $log
+ */
+ public function addLog( Log $log ) {
+ $this->logs[$log->getId()] = $log;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return bool
+ */
+ public function hasLogWithId( $id ){
+ return array_key_exists( $id, $this->logs );
+ }
+
+ /**
+ * @param Log $log
+ *
+ * @return bool
+ */
+ public function hasLog( Log $log ){
+ return array_key_exists( $log->getId(), $this->logs );
+ }
+
+ /**
+ * @return Log|null Log or null if there is no log
+ */
+ public function getLatest() {
+ if( empty( $this->logs ) ) {
+ return null;
+ }
+ return $this->logs[ max( array_keys( $this->logs ) ) ];
+ }
+
+ /**
+ * @since 0.6
+ * @return Log|null Log or null if there is no log
+ */
+ public function getOldest() {
+ if( empty( $this->logs ) ) {
+ return null;
+ }
+ return $this->logs[ min( array_keys( $this->logs ) ) ];
+ }
+
+ /**
+ * @since 0.6
+ * @return bool
+ */
+ public function isEmpty() {
+ return empty( $this->logs );
+ }
+
+ /**
+ * @param int $id
+ *
+ * @throws RuntimeException
+ * @return Log
+ */
+ public function get( $id ){
+ if( $this->hasLogWithId( $id ) ){
+ return $this->logs[$id];
+ }
+ throw new RuntimeException( 'No such Log loaded in LogList object' );
+ }
+
+ /**
+ * @return Log[]
+ */
+ public function toArray() {
+ return $this->logs;
+ }
+
+ /**
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ public function jsonSerialize() {
+ return $this->toArray();
+ }
+
+ /**
+ * @param array $json
+ *
+ * @return self
+ */
+ public static function jsonDeserialize( $json ) {
+ $self = new LogList();
+ foreach ( $json as $logJson ) {
+ $self->addLog( Log::jsonDeserialize( $logJson ) );
+ }
+ return $self;
+ }
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/NamespaceInfo.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/NamespaceInfo.php
new file mode 100644
index 00000000..451ec972
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/NamespaceInfo.php
@@ -0,0 +1,131 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+/**
+ * Class representing metadata about a MediaWiki namespace
+ *
+ * @author gbirke
+ */
+class NamespaceInfo
+{
+ /**
+ * @var int
+ */
+ private $id;
+
+ /**
+ * @var string
+ */
+ private $canonicalName;
+
+ /**
+ * @var string
+ */
+ private $localName;
+
+ /**
+ * @var string
+ */
+ private $caseHandling;
+
+ /**
+ * @var string
+ */
+ private $defaultContentModel;
+
+ /**
+ * @var array
+ */
+ private $aliases;
+
+ /**
+ * NamespaceInfo constructor.
+ * @param int $id
+ * @param string $canonicalName
+ * @param string $localName
+ * @param string $caseHandling
+ * @param string $defaultContentModel
+ * @param array $aliases
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $id, $canonicalName, $localName, $caseHandling, $defaultContentModel = null, $aliases = [] )
+ {
+ if( !is_int( $id ) ) {
+ throw new \InvalidArgumentException( '$id must be an integer' );
+ }
+ if ( !is_string( $canonicalName ) ) {
+ throw new \InvalidArgumentException( '$canonicalName must be a string' );
+ }
+ if ( !is_string( $localName ) ) {
+ throw new \InvalidArgumentException( '$localName must be a string' );
+ }
+ if ( !is_string( $caseHandling ) ) {
+ throw new \InvalidArgumentException( '$caseHandling must be a string' );
+ }
+ if ( !is_null( $defaultContentModel) && !is_string( $defaultContentModel ) ) {
+ throw new \InvalidArgumentException( '$canonicalName must be a string' );
+ }
+
+ if ( !is_array( $aliases ) ) {
+ throw new \InvalidArgumentException( '$aliases must be an array' );
+ }
+
+ $this->id = $id;
+ $this->canonicalName = $canonicalName;
+ $this->localName = $localName;
+ $this->caseHandling = $caseHandling;
+ $this->defaultContentModel = $defaultContentModel;
+ $this->aliases = $aliases;
+ }
+
+ /**
+ * @return int
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCanonicalName()
+ {
+ return $this->canonicalName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLocalName()
+ {
+ return $this->localName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getCaseHandling()
+ {
+ return $this->caseHandling;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDefaultContentModel()
+ {
+ return $this->defaultContentModel;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAliases()
+ {
+ return $this->aliases;
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Page.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Page.php
new file mode 100644
index 00000000..c3951e85
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Page.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+
+class Page {
+
+ /**
+ * @var Revisions
+ */
+ private $revisions;
+
+ /**
+ * @var PageIdentifier
+ */
+ private $pageIdentifier;
+
+ /**
+ * @param PageIdentifier $pageIdentifier
+ * @param Revisions|null $revisions
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( PageIdentifier $pageIdentifier = null , Revisions $revisions = null ) {
+ if( is_null( $revisions ) ) {
+ $revisions = new Revisions();
+ }
+ $this->revisions = $revisions;
+ $this->pageIdentifier = $pageIdentifier;
+ }
+
+ /**
+ * @deprecated since 0.5
+ * @return int
+ */
+ public function getId() {
+ return $this->pageIdentifier->getId();
+ }
+
+ /**
+ * @return Revisions
+ */
+ public function getRevisions() {
+ return $this->revisions;
+ }
+
+ /**
+ * @deprecated since 0.5
+ * @return Title
+ */
+ public function getTitle() {
+ return $this->pageIdentifier->getTitle();
+ }
+
+ /**
+ * @return PageIdentifier
+ */
+ public function getPageIdentifier() {
+ return $this->pageIdentifier;
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/PageIdentifier.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/PageIdentifier.php
new file mode 100644
index 00000000..528e3c88
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/PageIdentifier.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+use JsonSerializable;
+
+class PageIdentifier implements JsonSerializable {
+
+ /**
+ * @var int|null
+ */
+ private $id;
+
+ /**
+ * @var Title|null
+ */
+ private $title;
+
+ /**
+ * @param Title|null $title
+ * @param int|null $id
+ * @throws InvalidArgumentException
+ */
+ public function __construct( Title $title = null, $id = null ) {
+ if( !is_int( $id ) && !is_null( $id ) ) {
+ throw new InvalidArgumentException( '$id must be an int' );
+ }
+ $this->title = $title;
+ $this->id = $id;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * @return Title|null
+ */
+ public function getTitle() {
+ return $this->title;
+ }
+
+ /**
+ * Does this object identify a page
+ * @return bool
+ */
+ public function identifiesPage() {
+ if( is_null( $this->title ) && is_null( $this->id ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ public function jsonSerialize() {
+ $array = array();
+ if ( $this->id !== null ) {
+ $array['id'] = $this->id;
+ }
+ if ( $this->title !== null ) {
+ $array['title'] = $this->title->jsonSerialize();
+ }
+ return $array;
+ }
+
+ /**
+ * @param array $array
+ *
+ * @returns self
+ */
+ public static function jsonDeserialize( $array ) {
+ return new self(
+ isset( $array['title'] ) ? Title::jsonDeserialize( $array['title'] ) : null,
+ isset( $array['id'] ) ? $array['id'] : null
+
+ );
+ }
+}
+ \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Pages.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Pages.php
new file mode 100644
index 00000000..b8c5614c
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Pages.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * Represents a collection or Page classes
+ * @author Addshore
+ */
+class Pages {
+
+ /**
+ * @var Page[]
+ */
+ private $pages;
+
+ /**
+ * @param Page[] $pages
+ */
+ public function __construct( $pages = array() ) {
+ $this->pages = array();
+ $this->addPages( $pages );
+ }
+
+ /**
+ * @param Page[]|Pages $pages
+ *
+ * @throws InvalidArgumentException
+ */
+ public function addPages( $pages ) {
+ if( !is_array( $pages ) && !$pages instanceof Pages ) {
+ throw new InvalidArgumentException( '$pages needs to either be an array or a Pages object' );
+ }
+ if( $pages instanceof Pages ) {
+ $pages = $pages->toArray();
+ }
+ foreach( $pages as $page ) {
+ $this->addPage( $page );
+ }
+ }
+
+ /**
+ * @param Page $page
+ */
+ public function addPage( Page $page ) {
+ $this->pages[$page->getId()] = $page;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return bool
+ */
+ public function hasPageWithId( $id ){
+ return array_key_exists( $id, $this->pages );
+ }
+
+ /**
+ * @param Page $page
+ *
+ * @return bool
+ */
+ public function hasPage( Page $page ){
+ return array_key_exists( $page->getId(), $this->pages );
+ }
+
+ /**
+ * @return Page|null Page or null if there is no page
+ */
+ public function getLatest() {
+ if( empty( $this->pages ) ) {
+ return null;
+ }
+ return $this->pages[ max( array_keys( $this->pages ) ) ];
+ }
+
+
+ /**
+ * @param int $pageid
+ *
+ * @throws RuntimeException
+ * @return Page
+ */
+ public function get( $pageid ){
+ if( $this->hasPageWithId( $pageid ) ){
+ return $this->pages[$pageid];
+ }
+ throw new RuntimeException( 'No such page loaded in Pages object' );
+ }
+
+ /**
+ * @return Page[]
+ */
+ public function toArray() {
+ return $this->pages;
+ }
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Redirect.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Redirect.php
new file mode 100644
index 00000000..55b8b607
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Redirect.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use JsonSerializable;
+
+class Redirect implements JsonSerializable {
+
+ private $from;
+ private $to;
+
+ public function __construct( Title $from, Title $to ) {
+ $this->from = $from;
+ $this->to = $to;
+ }
+
+ /**
+ * @return Title
+ */
+ public function getFrom() {
+ return $this->from;
+ }
+
+ /**
+ * @return Title
+ */
+ public function getTo() {
+ return $this->to;
+ }
+
+ /**
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ public function jsonSerialize() {
+ return array(
+ 'from' => $this->from->jsonSerialize(),
+ 'to' => $this->to->jsonSerialize(),
+ );
+ }
+
+ /**
+ * @param array $json
+ *
+ * @return self
+ */
+ public static function jsonDeserialize( $json ) {
+ return new self(
+ Title::jsonDeserialize( $json['from'] ),
+ Title::jsonDeserialize( $json['to'] )
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revision.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revision.php
new file mode 100644
index 00000000..09afe63f
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revision.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+/**
+ * Representation of a version of content
+ * @author Addshore
+ */
+class Revision {
+
+ /**
+ * @var int Id of the revision
+ */
+ private $id;
+
+ /**
+ * @var PageIdentifier of the page for the revision
+ */
+ private $pageIdentifier;
+
+ /**
+ * @var Content
+ */
+ private $content;
+
+ /**
+ * @var EditInfo
+ */
+ private $editInfo;
+
+ /**
+ * @var null|string
+ */
+ private $user;
+
+ /**
+ * @var null|string
+ */
+ private $timestamp;
+
+ /**
+ * @param Content $content
+ * @param PageIdentifier|null $pageIdentifier
+ * @param int|null $revId
+ * @param EditInfo|null $editInfo
+ * @param string|null $user
+ * @param string|null $timestamp
+ */
+ public function __construct( Content $content, PageIdentifier $pageIdentifier = null, $revId = null, EditInfo $editInfo = null, $user = null, $timestamp = null ) {
+ if( is_null( $editInfo ) ) {
+ $editInfo = new EditInfo();
+ }
+ if( is_null( $pageIdentifier ) ) {
+ $pageIdentifier = new PageIdentifier();
+ }
+ $this->content = $content;
+ $this->pageIdentifier = $pageIdentifier;
+ $this->id = $revId;
+ $this->editInfo = $editInfo;
+ $this->user = $user;
+ $this->timestamp = $timestamp;
+ }
+
+ /**
+ * @return Content
+ */
+ public function getContent() {
+ return $this->content;
+ }
+
+ /**
+ * @return EditInfo
+ */
+ public function getEditInfo() {
+ return $this->editInfo;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * @return PageIdentifier|null
+ */
+ public function getPageIdentifier() {
+ return $this->pageIdentifier;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getUser() {
+ return $this->user;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getTimestamp() {
+ return $this->timestamp;
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revisions.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revisions.php
new file mode 100644
index 00000000..c6d2f436
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Revisions.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * Represents a collection or revisions
+ * @author Addshore
+ */
+class Revisions {
+
+ /**
+ * @var Revision[]
+ */
+ private $revisions;
+
+ /**
+ * @param Revisions[] $revisions
+ */
+ public function __construct( $revisions = array() ) {
+ $this->revisions = array();
+ $this->addRevisions( $revisions );
+ }
+
+ /**
+ * @param Revision[]|Revisions $revisions
+ *
+ * @throws InvalidArgumentException
+ */
+ public function addRevisions( $revisions ) {
+ if( !is_array( $revisions ) && !$revisions instanceof Revisions ) {
+ throw new InvalidArgumentException( '$revisions needs to either be an array or a Revisions object' );
+ }
+ if( $revisions instanceof Revisions ) {
+ $revisions = $revisions->toArray();
+ }
+ foreach( $revisions as $revision ) {
+ $this->addRevision( $revision );
+ }
+ }
+
+ /**
+ * @param Revision $revision
+ */
+ public function addRevision( Revision $revision ) {
+ $this->revisions[$revision->getId()] = $revision;
+ }
+
+ /**
+ * @param int $id
+ *
+ * @return bool
+ */
+ public function hasRevisionWithId( $id ){
+ return array_key_exists( $id, $this->revisions );
+ }
+
+ /**
+ * @param Revision $revision
+ *
+ * @return bool
+ */
+ public function hasRevision( Revision $revision ){
+ return array_key_exists( $revision->getId(), $this->revisions );
+ }
+
+ /**
+ * @return Revision|null Revision or null if there is no revision
+ */
+ public function getLatest() {
+ if( empty( $this->revisions ) ) {
+ return null;
+ }
+ return $this->revisions[ max( array_keys( $this->revisions ) ) ];
+ }
+
+ /**
+ * @param int $revid
+ *
+ * @throws RuntimeException
+ * @throws InvalidArgumentException
+ * @return Revision
+ */
+ public function get( $revid ){
+ if( !is_int( $revid ) ) {
+ throw new InvalidArgumentException( '$revid needs to be an int' );
+ }
+ if( $this->hasRevisionWithId( $revid ) ){
+ return $this->revisions[$revid];
+ }
+ throw new RuntimeException( 'No such revision loaded in Revisions object' );
+ }
+
+ /**
+ * @return Revision[]
+ */
+ public function toArray() {
+ return $this->revisions;
+ }
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Title.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Title.php
new file mode 100644
index 00000000..1fb15138
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/Title.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+use JsonSerializable;
+
+/**
+ * @author Addshore
+ */
+class Title implements JsonSerializable {
+
+ /**
+ * @var string
+ */
+ private $title;
+
+ /**
+ * @var int
+ */
+ private $ns;
+
+ /**
+ * @param string $title
+ * @param int $ns
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $title, $ns = 0 ) {
+ if( !is_string( $title ) ) {
+ throw new InvalidArgumentException( '$title must be a string' );
+ }
+ if( !is_int( $ns ) ) {
+ throw new InvalidArgumentException( '$ns must be an int' );
+ }
+ $this->title = $title;
+ $this->ns = $ns;
+ }
+
+ /**
+ * @return int
+ * @since 0.1
+ */
+ public function getNs() {
+ return $this->ns;
+ }
+
+ /**
+ * @return string
+ * @since 0.6
+ */
+ public function getText() {
+ return $this->title;
+ }
+
+ /**
+ * @return string
+ * @deprecated in 0.6 use getText (makes things look cleaner)
+ */
+ public function getTitle() {
+ return $this->getText();
+ }
+
+ /**
+ * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
+ */
+ public function jsonSerialize() {
+ return array(
+ 'title' => $this->title,
+ 'ns' => $this->ns,
+ );
+ }
+
+ /**
+ * @param array $json
+ *
+ * @return self
+ */
+ public static function jsonDeserialize( $json ) {
+ return new self( $json['title'], $json['ns'] );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/User.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/User.php
new file mode 100644
index 00000000..fca5ddf3
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/src/User.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Mediawiki\DataModel;
+
+use InvalidArgumentException;
+
+/**
+ * Represents a mediawiki user
+ * @author Addshore
+ */
+class User {
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var int
+ */
+ private $id;
+
+ /**
+ * @var int
+ */
+ private $editcount;
+
+ /**
+ * @var string
+ */
+ private $registration;
+
+ /**
+ * @var array
+ */
+ private $groups;
+
+ /**
+ * @var array
+ */
+ private $rights;
+
+ /**
+ * @var string
+ */
+ private $gender;
+
+ /**
+ * @param string $name
+ * @param int $id
+ * @param int $editcount
+ * @param string $registration
+ * @param array[] $groups groups grouped by type.
+ * Keys to use are 'groups' and 'implicitgroups' as returned by the api.
+ * @param array $rights
+ * @param string $gender
+ *
+ * @throws InvalidArgumentException
+ */
+ public function __construct( $name, $id, $editcount, $registration, $groups, $rights, $gender ) {
+ if( !is_string( $name ) || empty( $name ) ) {
+ throw new InvalidArgumentException( '$name must be a string and can not be empty' );
+ }
+ if( !is_int( $id ) ) {
+ throw new InvalidArgumentException( '$id must be an int' );
+ }
+ if( !is_int( $editcount ) ) {
+ throw new InvalidArgumentException( '$editcount must be an int' );
+ }
+ if( !is_array( $groups ) || !array_key_exists( 'groups', $groups ) || !array_key_exists( 'implicitgroups', $groups ) ) {
+ throw new InvalidArgumentException( '$groups must be an array or arrays with keys "groups" and "implicitgroups"' );
+ }
+ if( !is_array( $rights ) ) {
+ throw new InvalidArgumentException( '$rights must be an array' );
+ }
+ if( !is_string( $gender ) ) {
+ throw new InvalidArgumentException( '$gender must be a string' );
+ }
+
+ $this->editcount = $editcount;
+ $this->gender = $gender;
+ $this->groups = $groups;
+ $this->id = $id;
+ $this->name = $name;
+ $this->registration = $registration;
+ $this->rights = $rights;
+ }
+
+ /**
+ * @return int
+ */
+ public function getEditcount() {
+ return $this->editcount;
+ }
+
+ /**
+ * @return string
+ */
+ public function getGender() {
+ return $this->gender;
+ }
+
+ /**
+ * @param string $type 'groups' or 'implicitgroups'
+ *
+ * @return array
+ */
+ public function getGroups( $type = 'groups' ) {
+ return $this->groups[$type];
+ }
+
+ /**
+ * @return int
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRegistration() {
+ return $this->registration;
+ }
+
+ /**
+ * @return array
+ */
+ public function getRights() {
+ return $this->rights;
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/ContentTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/ContentTest.php
new file mode 100644
index 00000000..e412749a
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/ContentTest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\Content;
+use PHPUnit_Framework_TestCase;
+
+class ContentTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $data, $model ) {
+ $content = new Content( $data, $model );
+ $this->assertEquals( $data, $content->getData() );
+ $this->assertEquals( $model, $content->getModel() );
+ $this->assertTrue( is_string( $content->getHash() ) );
+ $this->assertFalse( $content->hasChanged() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( '', null ),
+ array( 'foo', null ),
+ array( new \stdClass(), null ),
+ );
+ }
+
+}
+ \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/EditInfoTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/EditInfoTest.php
new file mode 100644
index 00000000..c3128bca
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/EditInfoTest.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\EditInfo;
+use PHPUnit_Framework_TestCase;
+
+/**
+ * @covers \Mediawiki\DataModel\EditInfo
+ * @author Addshore
+ */
+class EditInfoTest extends PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $sum, $minor, $bot ) {
+ $flags = new EditInfo( $sum, $minor, $bot );
+ $this->assertEquals( $sum, $flags->getSummary() );
+ $this->assertEquals( $minor, $flags->getMinor() );
+ $this->assertEquals( $bot, $flags->getBot() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( '', EditInfo::MINOR, EditInfo::BOT ),
+ array( '', EditInfo::MINOR, EditInfo::NOTBOT ),
+ array( '', EditInfo::NOTMINOR, EditInfo::BOT ),
+ array( '', EditInfo::NOTMINOR, EditInfo::NOTBOT ),
+ array( 'FOO', EditInfo::NOTMINOR, EditInfo::NOTBOT ),
+ );
+ }
+
+ /**
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction( $sum, $minor, $bot ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new EditInfo( $sum, $minor, $bot );
+ }
+
+ public function provideInvalidConstruction() {
+ return array(
+ array( 1, 2, 3 ),
+ array( "foo", false, 3 ),
+ array( "foo", 3, false ),
+ array( array(), true, false ),
+ );
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/FileTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/FileTest.php
new file mode 100644
index 00000000..0da77bd1
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/FileTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\File;
+use Mediawiki\DataModel\PageIdentifier;
+
+/**
+ * @covers \Mediawiki\DataModel\File
+ * @author Addshore
+ */
+class FileTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $url ) {
+ $file = new File(
+ $url,
+ new PageIdentifier( $this->newMockTitle(), 1 ),
+ $this->newMockRevisions()
+ );
+ $this->assertEquals( $url, $file->getUrl() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( 'http://upload.wikimedia.org/wikipedia/en/3/39/Journal_of_Geek_Studies_-_logo.jpg' ),
+ );
+ }
+
+ private function newMockTitle() {
+ return $this->getMockBuilder( '\Mediawiki\DataModel\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ private function newMockRevisions() {
+ return $this->getMockBuilder( '\Mediawiki\DataModel\Revisions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/LogListTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/LogListTest.php
new file mode 100644
index 00000000..1098a8a6
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/LogListTest.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\Log;
+use Mediawiki\DataModel\LogList;
+use Mediawiki\DataModel\PageIdentifier;
+
+/**
+ * @covers \Mediawiki\DataModel\LogList
+ * @author Addshore
+ */
+class LogListTest extends \PHPUnit_Framework_TestCase {
+
+ public function testJsonRoundTrip() {
+ $logList = new LogList( array(
+ new Log( 1, 'ty', 'ac', '2014', 'Addshore', new PageIdentifier( null, 22 ), 'comment', array() ),
+ new Log( 2, 'ty2', 'ac2', '2015', 'Addbot', new PageIdentifier( null, 33 ), 'comment2', array() ),
+ ) );
+ $json = $logList->jsonSerialize();
+ $json = json_decode( json_encode( $json ), true );
+ $this->assertEquals( $logList, LogList::jsonDeserialize( $json ) );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/NamespaceInfoTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/NamespaceInfoTest.php
new file mode 100644
index 00000000..4587a8a0
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/NamespaceInfoTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\NamespaceInfo;
+
+/**
+ * @covers \Mediawiki\DataModel\NamespaceInfo
+ * @author gbirke
+ */
+class NamespaceInfoTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @dataProvider provideValidConstruction
+ * @param int $id
+ * @param string $canonicalName
+ * @param string $localName
+ * @param string $caseHandling
+ * @param null $defaultContentModel
+ * @param array $aliases
+ */
+ public function testValidConstruction($id, $canonicalName, $localName, $caseHandling, $defaultContentModel = null,
+ $aliases = [] ) {
+ $namespace = new NamespaceInfo( $id, $canonicalName, $localName, $caseHandling, $defaultContentModel, $aliases );
+ $this->assertSame( $id, $namespace->getId() );
+ $this->assertSame( $canonicalName, $namespace->getCanonicalName() );
+ $this->assertSame( $localName, $namespace->getLocalName() );
+ $this->assertSame( $caseHandling, $namespace->getCaseHandling() );
+ $this->assertSame( $defaultContentModel, $namespace->getDefaultContentModel() );
+ $this->assertSame( $aliases, $namespace->getAliases() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( -2, 'Media', 'Media', 'first-letter' ),
+ array( 0, '', '', 'first-letter' ),
+ array( 4, 'Project', 'Wikipedia', 'first-letter' ),
+ array( 2302, 'Gadget definition', 'Gadget definition', 'case-sensitive', 'GadgetDefinition' ),
+ array( 2302, 'Gadget definition', 'Gadget definition', 'case-sensitive', 'GadgetDefinition', [ 'GD' ] ),
+ );
+ }
+
+ /**
+ * @param $id
+ * @param $canonicalName
+ * @param $localName
+ * @param $caseHandling
+ * @param null $defaultContentModel
+ * @param array $aliases
+ *
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction($id, $canonicalName, $localName, $caseHandling, $defaultContentModel = null,
+ $aliases = [] ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new NamespaceInfo( $id, $canonicalName, $localName, $caseHandling, $defaultContentModel, $aliases );
+ }
+
+ public function provideInvalidConstruction() {
+ return array(
+ array( .5, 'Media', 'Media', 'first-letter' ),
+ array( '0', '', '', 'first-letter' ),
+ array( -2, null, 'Media', 'first-letter' ),
+ array( -2, 'Media', null, 'first-letter' ),
+ array( 4, 'Project', 'Wikipedia', 'first-letter', 5 ),
+ array( 2302, null, 'Gadget definition', 'case-sensitive', 'GadgetDefinition' ),
+ array( 4, 'Project', 'Wikipedia', 'first-letter', 5 ),
+ array( 4, 'Project', 'Wikipedia', 'first-letter', 'GadgetDefinition', 'notanalias' ),
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageIdentifierTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageIdentifierTest.php
new file mode 100644
index 00000000..42aa8412
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageIdentifierTest.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @covers Mediawiki\DataModel\PageIdentifier
+ */
+class PageIdentifierTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $title, $pageid, $identifiesPage ) {
+ $pageIdentifier = new PageIdentifier( $title, $pageid );
+ if( is_string( $title ) ) {
+ $this->assertEquals( new Title( $title ), $pageIdentifier->getTitle() );
+ } else {
+ $this->assertEquals( $title, $pageIdentifier->getTitle() );
+ }
+ $this->assertEquals( $pageid, $pageIdentifier->getId() );
+ $this->assertEquals( $identifiesPage, $pageIdentifier->identifiesPage() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( null, null, false ),
+ array( new Title( 'Foo' ), null, true ),
+ array( new Title( 'Foo', 2 ), null, true ),
+ array( null, 3, true ),
+ );
+ }
+
+ public function provideRoundTripObjects() {
+ return array(
+ array( new PageIdentifier( null, null ) ),
+ array( new PageIdentifier( null, 44 ) ),
+ array( new PageIdentifier( new Title( 'someTitle', 12 ), null ) ),
+ array( new PageIdentifier( new Title( 'someTitle', 55 ), 99 ) ),
+ );
+ }
+
+ /**
+ * @dataProvider provideRoundTripObjects
+ */
+ public function testJsonRoundTrip( PageIdentifier $identifierObject ) {
+ $json = $identifierObject->jsonSerialize();
+ $this->assertEquals( $identifierObject, PageIdentifier::jsonDeserialize( $json ) );
+ }
+
+}
+ \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageTest.php
new file mode 100644
index 00000000..cbc31c8c
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PageTest.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+
+/**
+ * @covers \Mediawiki\DataModel\Page
+ * @author Addshore
+ */
+class PageTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $pageIdentifier, $revisions ) {
+ $page = new Page( $pageIdentifier, $revisions );
+ $this->assertEquals( $pageIdentifier, $page->getPageIdentifier() );
+ if( is_null( $revisions ) ) {
+ $this->assertInstanceOf( 'Mediawiki\DataModel\Revisions', $page->getRevisions() );
+ } else {
+ $this->assertEquals( $revisions, $page->getRevisions() );
+ }
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( null, null ),
+ array( null, $this->newMockRevisions() ),
+ array( new PageIdentifier( $this->newMockTitle(), 1 ), $this->newMockRevisions() ),
+ array( new PageIdentifier( $this->newMockTitle(), 123 ), null ),
+ );
+ }
+
+ private function newMockTitle() {
+ return $this->getMockBuilder( '\Mediawiki\DataModel\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ private function newMockRevisions() {
+ return $this->getMockBuilder( '\Mediawiki\DataModel\Revisions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PagesTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PagesTest.php
new file mode 100644
index 00000000..0055ecc7
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/PagesTest.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\Page;
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Pages;
+
+/**
+ * @covers \Mediawiki\DataModel\Pages
+ * @author Addshore
+ */
+class PagesTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $input, $expected ) {
+ $pages = new Pages( $input );
+ $this->assertEquals( $expected, $pages->toArray() );
+ }
+
+ public function provideValidConstruction() {
+ $mockTitle = $this->getMockBuilder( 'Mediawiki\DataModel\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockRevisions = $this->getMockBuilder( 'Mediawiki\DataModel\Revisions' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ //todo mock these
+ $page1 = new Page( new PageIdentifier( $mockTitle, 1 ), $mockRevisions );
+ $page2 = new Page( new PageIdentifier( $mockTitle, 2 ), $mockRevisions );
+ $page4 = new Page( new PageIdentifier( $mockTitle, 4 ), $mockRevisions );
+
+ return array(
+ array( array( $page1 ), array( 1 => $page1 ) ),
+ array( array( $page2, $page1 ), array( 1 => $page1, 2 => $page2 ) ),
+ array( array( $page4, $page1 ), array( 1 => $page1, 4 => $page4 ) ),
+ array( new Pages( array( $page4, $page1 ) ), array( 1 => $page1, 4 => $page4 ) ),
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RedirectTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RedirectTest.php
new file mode 100644
index 00000000..0f561e92
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RedirectTest.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\Redirect;
+use Mediawiki\DataModel\Title;
+
+/**
+ * @covers \Mediawiki\DataModel\Redirect
+ * @author Addshore
+ */
+class RedirectTest extends \PHPUnit_Framework_TestCase {
+
+ public function testJsonRoundTrip() {
+ $title = new Redirect( new Title( 'Foo', 12 ), new Title( 'bar', 13 ) );
+ $json = $title->jsonSerialize();
+ $this->assertEquals( $title, Redirect::jsonDeserialize( $json ) );
+ }
+
+}
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionTest.php
new file mode 100644
index 00000000..e6260acb
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+
+/**
+ * @covers \Mediawiki\DataModel\Revision
+ * @author Addshore
+ */
+class RevisionTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $content, $pageIdentifier, $id, $editInfo, $user, $timestamp ) {
+ $rev = new Revision( $content, $pageIdentifier, $id, $editInfo, $user, $timestamp );
+ $this->assertEquals( $content, $rev->getContent() );
+ if( !is_null( $pageIdentifier ) ) {
+ $this->assertEquals( $pageIdentifier, $rev->getPageIdentifier() );
+ } else {
+ $this->assertInstanceOf( '\Mediawiki\DataModel\PageIdentifier', $rev->getPageIdentifier() );
+ }
+
+ $this->assertEquals( $id, $rev->getId() );
+ if( !is_null( $editInfo ) ) {
+ $this->assertEquals( $editInfo, $rev->getEditInfo() );
+ } else {
+ $this->assertInstanceOf( '\Mediawiki\DataModel\EditInfo', $rev->getEditInfo() );
+ }
+ $this->assertEquals( $user, $rev->getUser() );
+ $this->assertEquals( $timestamp, $rev->getTimestamp() );
+ }
+
+ public function provideValidConstruction() {
+ $mockContent = $this->getMockBuilder( 'Mediawiki\DataModel\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockEditInfo = $this->getMockBuilder( '\Mediawiki\DataModel\EditInfo' )
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockTitle = $this->getMockBuilder( 'Mediawiki\DataModel\Title' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ return array(
+ array( $mockContent, null, null, null, null, null ),
+ array( $mockContent, new PageIdentifier( null, 1 ), null , null, null,null ),
+ array( $mockContent, new PageIdentifier( null, 1 ), 1 , null, null, null ),
+ array( $mockContent, new PageIdentifier( null, 2 ), 1 , $mockEditInfo, null, null ),
+ array( $mockContent, new PageIdentifier( $mockTitle ), 1 , $mockEditInfo, 'foo', null ),
+ array( $mockContent, new PageIdentifier( $mockTitle, 3 ), 1 , $mockEditInfo, 'foo', '20141212121212' ),
+ );
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionsTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionsTest.php
new file mode 100644
index 00000000..6b7afbed
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/RevisionsTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\PageIdentifier;
+use Mediawiki\DataModel\Revision;
+use Mediawiki\DataModel\Revisions;
+
+/**
+ * @covers \Mediawiki\DataModel\Revisions
+ * @author Addshore
+ */
+class RevisionsTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $input, $expected ) {
+ $revisions = new Revisions( $input );
+ $this->assertEquals( $expected, $revisions->toArray() );
+ }
+
+ public function provideValidConstruction() {
+ $mockContent = $this->getMockBuilder( 'Mediawiki\DataModel\Content' )
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ //todo mock these
+ $rev1 = new Revision( $mockContent, new PageIdentifier( null, 1 ), 1 );
+ $rev2 = new Revision( $mockContent, new PageIdentifier( null, 1 ), 2 );
+ $rev4 = new Revision( $mockContent, new PageIdentifier( null, 1 ), 4 );
+
+ return array(
+ array( array( $rev1 ), array( 1 => $rev1 ) ),
+ array( array( $rev2, $rev1 ), array( 1 => $rev1, 2 => $rev2 ) ),
+ array( array( $rev4, $rev1 ), array( 1 => $rev1, 4 => $rev4 ) ),
+ array( new Revisions( array( $rev4, $rev1 ) ), array( 1 => $rev1, 4 => $rev4 ) ),
+ );
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/TitleTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/TitleTest.php
new file mode 100644
index 00000000..2c4d73da
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/TitleTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\Title;
+
+/**
+ * @covers \Mediawiki\DataModel\Title
+ * @author Addshore
+ */
+class TitleTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $title, $ns ) {
+ $titleObj = new Title( $title, $ns );
+ $this->assertEquals( $title, $titleObj->getText() );
+ $this->assertEquals( $title, $titleObj->getTitle() );
+ $this->assertEquals( $ns, $titleObj->getNs() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( 'fooo', 0 ),
+ array( 'Foo:Bar', 15 ),
+ array( 'FooBar:Bar', 9999 ),
+ );
+ }
+
+ /**
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction( $title, $ns ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new Title( $title, $ns );
+ }
+
+ public function provideInvalidConstruction() {
+ return array(
+ array( array(), array() ),
+ array( 'foo', array() ),
+ array( array(), 1 ),
+ array( null, 1 ),
+ array( null, null ),
+ array( 'foo', null ),
+ );
+ }
+
+ public function testJsonRoundTrip() {
+ $title = new Title( 'Foo', 19 );
+ $json = $title->jsonSerialize();
+ $this->assertEquals( $title, Title::jsonDeserialize( $json ) );
+ }
+
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/UserTest.php b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/UserTest.php
new file mode 100644
index 00000000..fcf89994
--- /dev/null
+++ b/bin/wiki/vendor/addwiki/mediawiki-datamodel/tests/UserTest.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Mediawiki\DataModel\Test;
+
+use Mediawiki\DataModel\User;
+
+/**
+ * @covers \Mediawiki\DataModel\User
+ * @author Addshore
+ */
+class UserTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @dataProvider provideValidConstruction
+ */
+ public function testValidConstruction( $name, $id, $editcount, $registration, $groups, $rights, $gender ) {
+ $user = new User( $name, $id, $editcount, $registration, $groups, $rights, $gender );
+ $this->assertEquals( $name, $user->getName() );
+ $this->assertEquals( $id, $user->getId() );
+ $this->assertEquals( $editcount, $user->getEditcount() );
+ $this->assertEquals( $registration, $user->getRegistration() );
+ $this->assertEquals( $groups['groups'], $user->getGroups() );
+ $this->assertEquals( $groups['implicitgroups'], $user->getGroups( 'implicitgroups' ) );
+ $this->assertEquals( $rights, $user->getRights() );
+ $this->assertEquals( $gender, $user->getGender() );
+ }
+
+ public function provideValidConstruction() {
+ return array(
+ array( 'Username', 1, 1, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'male' ),
+ array( 'Username', 1, 1, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'female' ),
+ array( 'Username', 99999999, 99999997, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'male' ),
+ array( 'Username', 1, 1, null, array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'female' ),
+ );
+ }
+
+ /**
+ * @dataProvider provideInvalidConstruction
+ */
+ public function testInvalidConstruction( $name, $id, $editcount, $registration, $groups, $rights, $gender ) {
+ $this->setExpectedException( 'InvalidArgumentException' );
+ new User( $name, $id, $editcount, $registration, $groups, $rights, $gender );
+ }
+
+ public function provideInvalidConstruction() {
+ return array(
+ array( 'Username', 1, 1, 'TIMESTAMP', 'bad', array(), 'male' ),
+ array( 'Username', 1, 1, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), 'bad', 'male' ),
+ array( 'Username', 1, 1, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 1 ),
+ array( 'Username', 1, 'bad', 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'male' ),
+ array( 'Username', 'bad', 1, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'male' ),
+ array( 14287941, 1, 1, 'TIMESTAMP', array( 'groups' => array(), 'implicitgroups' => array() ), array(), 'male' ),
+ array( 'Username', 1, 1, 'TIMESTAMP', array( 'groups' => array(), 'foo' => array() ), array(), 'male' ),
+ array( 'Username', 1, 1, 'TIMESTAMP', array( 'groups' => array() ), array(), 'male' ),
+ array( 'Username', 1, 1, 'TIMESTAMP', array(), array(), 'male' ),
+ );
+ }
+
+}
diff --git a/bin/wiki/vendor/autoload.php b/bin/wiki/vendor/autoload.php
new file mode 100644
index 00000000..99420711
--- /dev/null
+++ b/bin/wiki/vendor/autoload.php
@@ -0,0 +1,7 @@
+<?php
+
+// autoload.php @generated by Composer
+
+require_once __DIR__ . '/composer/autoload_real.php';
+
+return ComposerAutoloaderInit1597705ddb4a19e7b99e0b2375ab438e::getLoader();
diff --git a/bin/wiki/vendor/composer/ClassLoader.php b/bin/wiki/vendor/composer/ClassLoader.php
new file mode 100644
index 00000000..2c72175e
--- /dev/null
+++ b/bin/wiki/vendor/composer/ClassLoader.php
@@ -0,0 +1,445 @@
+<?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, PSR-4 and classmap class loader.
+ *
+ * $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>
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+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;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ 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-4 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;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * 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)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath.'\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ $length = $this->prefixLengthsPsr4[$first][$search];
+ 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;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/bin/wiki/vendor/composer/LICENSE b/bin/wiki/vendor/composer/LICENSE
new file mode 100644
index 00000000..f27399a0
--- /dev/null
+++ b/bin/wiki/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/bin/wiki/vendor/composer/autoload_classmap.php b/bin/wiki/vendor/composer/autoload_classmap.php
new file mode 100644
index 00000000..7a91153b
--- /dev/null
+++ b/bin/wiki/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_classmap.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/bin/wiki/vendor/composer/autoload_files.php b/bin/wiki/vendor/composer/autoload_files.php
new file mode 100644
index 00000000..03507c9c
--- /dev/null
+++ b/bin/wiki/vendor/composer/autoload_files.php
@@ -0,0 +1,13 @@
+<?php
+
+// autoload_files.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
+ '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
+ 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
+ '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
+);
diff --git a/bin/wiki/vendor/composer/autoload_namespaces.php b/bin/wiki/vendor/composer/autoload_namespaces.php
new file mode 100644
index 00000000..b7fc0125
--- /dev/null
+++ b/bin/wiki/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+<?php
+
+// autoload_namespaces.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+);
diff --git a/bin/wiki/vendor/composer/autoload_psr4.php b/bin/wiki/vendor/composer/autoload_psr4.php
new file mode 100644
index 00000000..bc85a820
--- /dev/null
+++ b/bin/wiki/vendor/composer/autoload_psr4.php
@@ -0,0 +1,16 @@
+<?php
+
+// autoload_psr4.php @generated by Composer
+
+$vendorDir = dirname(dirname(__FILE__));
+$baseDir = dirname($vendorDir);
+
+return array(
+ 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src'),
+ 'Mediawiki\\DataModel\\' => array($vendorDir . '/addwiki/mediawiki-datamodel/src'),
+ 'Mediawiki\\Api\\' => array($vendorDir . '/addwiki/mediawiki-api-base/src', $vendorDir . '/addwiki/mediawiki-api/src'),
+ 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
+ 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
+ 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
+);
diff --git a/bin/wiki/vendor/composer/autoload_real.php b/bin/wiki/vendor/composer/autoload_real.php
new file mode 100644
index 00000000..10b15c45
--- /dev/null
+++ b/bin/wiki/vendor/composer/autoload_real.php
@@ -0,0 +1,70 @@
+<?php
+
+// autoload_real.php @generated by Composer
+
+class ComposerAutoloaderInit1597705ddb4a19e7b99e0b2375ab438e
+{
+ 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('ComposerAutoloaderInit1597705ddb4a19e7b99e0b2375ab438e', 'loadClassLoader'), true, true);
+ self::$loader = $loader = new \Composer\Autoload\ClassLoader();
+ spl_autoload_unregister(array('ComposerAutoloaderInit1597705ddb4a19e7b99e0b2375ab438e', 'loadClassLoader'));
+
+ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInit1597705ddb4a19e7b99e0b2375ab438e::getInitializer($loader));
+ } else {
+ $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);
+
+ if ($useStaticLoader) {
+ $includeFiles = Composer\Autoload\ComposerStaticInit1597705ddb4a19e7b99e0b2375ab438e::$files;
+ } else {
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ }
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequire1597705ddb4a19e7b99e0b2375ab438e($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
+
+function composerRequire1597705ddb4a19e7b99e0b2375ab438e($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
+}
diff --git a/bin/wiki/vendor/composer/autoload_static.php b/bin/wiki/vendor/composer/autoload_static.php
new file mode 100644
index 00000000..71b15755
--- /dev/null
+++ b/bin/wiki/vendor/composer/autoload_static.php
@@ -0,0 +1,75 @@
+<?php
+
+// autoload_static.php @generated by Composer
+
+namespace Composer\Autoload;
+
+class ComposerStaticInit1597705ddb4a19e7b99e0b2375ab438e
+{
+ public static $files = array (
+ 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
+ '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
+ 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
+ '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'P' =>
+ array (
+ 'Psr\\Log\\' => 8,
+ 'Psr\\Http\\Message\\' => 17,
+ ),
+ 'M' =>
+ array (
+ 'Mediawiki\\DataModel\\' => 20,
+ 'Mediawiki\\Api\\' => 14,
+ ),
+ 'G' =>
+ array (
+ 'GuzzleHttp\\Psr7\\' => 16,
+ 'GuzzleHttp\\Promise\\' => 19,
+ 'GuzzleHttp\\' => 11,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Psr\\Log\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/log/Psr/Log',
+ ),
+ 'Psr\\Http\\Message\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/psr/http-message/src',
+ ),
+ 'Mediawiki\\DataModel\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/addwiki/mediawiki-datamodel/src',
+ ),
+ 'Mediawiki\\Api\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/addwiki/mediawiki-api-base/src',
+ 1 => __DIR__ . '/..' . '/addwiki/mediawiki-api/src',
+ ),
+ 'GuzzleHttp\\Psr7\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
+ ),
+ 'GuzzleHttp\\Promise\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/guzzlehttp/promises/src',
+ ),
+ 'GuzzleHttp\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
+ ),
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit1597705ddb4a19e7b99e0b2375ab438e::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit1597705ddb4a19e7b99e0b2375ab438e::$prefixDirsPsr4;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/bin/wiki/vendor/composer/installed.json b/bin/wiki/vendor/composer/installed.json
new file mode 100644
index 00000000..b7b07b4f
--- /dev/null
+++ b/bin/wiki/vendor/composer/installed.json
@@ -0,0 +1,489 @@
+[
+ {
+ "name": "addwiki/mediawiki-datamodel",
+ "version": "0.7.1",
+ "version_normalized": "0.7.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/addwiki/mediawiki-datamodel.git",
+ "reference": "05dd783715a92ec5449bab4091c0482cf3fcface"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/addwiki/mediawiki-datamodel/zipball/05dd783715a92ec5449bab4091c0482cf3fcface",
+ "reference": "05dd783715a92ec5449bab4091c0482cf3fcface",
+ "shasum": ""
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "phpunit/phpunit": "~4.8.0|~5.3.0"
+ },
+ "time": "2018-01-10T19:14:13+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\DataModel\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "description": "A Mediawiki datamodel",
+ "keywords": [
+ "mediawiki"
+ ]
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.0",
+ "version_normalized": "1.1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+ "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2018-11-20T15:27:04+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ]
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "v1.3.1",
+ "version_normalized": "1.3.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+ "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0"
+ },
+ "time": "2016-12-20T10:07:11+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ]
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "2.0.5",
+ "version_normalized": "2.0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+ "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~3.7.0",
+ "satooshi/php-coveralls": ">=1.0"
+ },
+ "time": "2016-02-11T07:05:27+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders."
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "version_normalized": "1.0.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2016-08-06T14:39:51+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ]
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.5.2",
+ "version_normalized": "1.5.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "9f83dded91781a01c63574e387eaa769be769115"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
+ "reference": "9f83dded91781a01c63574e387eaa769be769115",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+ },
+ "time": "2018-12-04T20:46:45+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ]
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "6.3.3",
+ "version_normalized": "6.3.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+ "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/promises": "^1.0",
+ "guzzlehttp/psr7": "^1.4",
+ "php": ">=5.5"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+ "psr/log": "^1.0"
+ },
+ "suggest": {
+ "psr/log": "Required for using the Log middleware"
+ },
+ "time": "2018-04-22T15:46:56+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.3-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ]
+ },
+ {
+ "name": "addwiki/mediawiki-api-base",
+ "version": "2.4.0",
+ "version_normalized": "2.4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/addwiki/mediawiki-api-base.git",
+ "reference": "33c147e91d05a48e953839fb3ad9e6386cfd85c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/addwiki/mediawiki-api-base/zipball/33c147e91d05a48e953839fb3ad9e6386cfd85c1",
+ "reference": "33c147e91d05a48e953839fb3ad9e6386cfd85c1",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "~6.0",
+ "guzzlehttp/promises": "~1.0",
+ "php": ">=5.5",
+ "psr/log": "~1.0"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "phpunit/phpunit": "~4.8.0|~5.3.0"
+ },
+ "suggest": {
+ "etsy/phan": "Allows running static analysis on the package (requires PHP 7+)"
+ },
+ "time": "2017-11-02T10:53:36+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.4.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "description": "A basic Mediawiki api base library",
+ "keywords": [
+ "mediawiki"
+ ]
+ },
+ {
+ "name": "addwiki/mediawiki-api",
+ "version": "0.7.2",
+ "version_normalized": "0.7.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/addwiki/mediawiki-api.git",
+ "reference": "f52fc3760d82774512d344e41c45c878a2c6659e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/addwiki/mediawiki-api/zipball/f52fc3760d82774512d344e41c45c878a2c6659e",
+ "reference": "f52fc3760d82774512d344e41c45c878a2c6659e",
+ "shasum": ""
+ },
+ "require": {
+ "addwiki/mediawiki-api-base": "~2.4",
+ "addwiki/mediawiki-datamodel": "~0.7.0"
+ },
+ "require-dev": {
+ "jakub-onderka/php-parallel-lint": "^0.9.2",
+ "mediawiki/mediawiki-codesniffer": "^13.0",
+ "monolog/monolog": "^1.23",
+ "phpunit/phpunit": "~4.8"
+ },
+ "time": "2017-11-20T03:08:06+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "0.7.x-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Mediawiki\\Api\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "authors": [
+ {
+ "name": "Addshore"
+ }
+ ],
+ "description": "A MediaWiki API library",
+ "keywords": [
+ "mediawiki"
+ ]
+ }
+]
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/CHANGELOG.md b/bin/wiki/vendor/guzzlehttp/guzzle/CHANGELOG.md
new file mode 100644
index 00000000..17badd75
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/CHANGELOG.md
@@ -0,0 +1,1287 @@
+# Change Log
+
+## 6.3.3 - 2018-04-22
+
+* Fix: Default headers when decode_content is specified
+
+
+## 6.3.2 - 2018-03-26
+
+* Fix: Release process
+
+
+## 6.3.1 - 2018-03-26
+
+* Bug fix: Parsing 0 epoch expiry times in cookies [#2014](https://github.com/guzzle/guzzle/pull/2014)
+* Improvement: Better ConnectException detection [#2012](https://github.com/guzzle/guzzle/pull/2012)
+* Bug fix: Malformed domain that contains a "/" [#1999](https://github.com/guzzle/guzzle/pull/1999)
+* Bug fix: Undefined offset when a cookie has no first key-value pair [#1998](https://github.com/guzzle/guzzle/pull/1998)
+* Improvement: Support PHPUnit 6 [#1953](https://github.com/guzzle/guzzle/pull/1953)
+* Bug fix: Support empty headers [#1915](https://github.com/guzzle/guzzle/pull/1915)
+* Bug fix: Ignore case during header modifications [#1916](https://github.com/guzzle/guzzle/pull/1916)
+
++ Minor code cleanups, documentation fixes and clarifications.
+
+
+## 6.3.0 - 2017-06-22
+
+* Feature: force IP resolution (ipv4 or ipv6) [#1608](https://github.com/guzzle/guzzle/pull/1608), [#1659](https://github.com/guzzle/guzzle/pull/1659)
+* Improvement: Don't include summary in exception message when body is empty [#1621](https://github.com/guzzle/guzzle/pull/1621)
+* Improvement: Handle `on_headers` option in MockHandler [#1580](https://github.com/guzzle/guzzle/pull/1580)
+* Improvement: Added SUSE Linux CA path [#1609](https://github.com/guzzle/guzzle/issues/1609)
+* Improvement: Use class reference for getting the name of the class instead of using hardcoded strings [#1641](https://github.com/guzzle/guzzle/pull/1641)
+* Feature: Added `read_timeout` option [#1611](https://github.com/guzzle/guzzle/pull/1611)
+* Bug fix: PHP 7.x fixes [#1685](https://github.com/guzzle/guzzle/pull/1685), [#1686](https://github.com/guzzle/guzzle/pull/1686), [#1811](https://github.com/guzzle/guzzle/pull/1811)
+* Deprecation: BadResponseException instantiation without a response [#1642](https://github.com/guzzle/guzzle/pull/1642)
+* Feature: Added NTLM auth [#1569](https://github.com/guzzle/guzzle/pull/1569)
+* Feature: Track redirect HTTP status codes [#1711](https://github.com/guzzle/guzzle/pull/1711)
+* Improvement: Check handler type during construction [#1745](https://github.com/guzzle/guzzle/pull/1745)
+* Improvement: Always include the Content-Length if there's a body [#1721](https://github.com/guzzle/guzzle/pull/1721)
+* Feature: Added convenience method to access a cookie by name [#1318](https://github.com/guzzle/guzzle/pull/1318)
+* Bug fix: Fill `CURLOPT_CAPATH` and `CURLOPT_CAINFO` properly [#1684](https://github.com/guzzle/guzzle/pull/1684)
+* Improvement: Use `\GuzzleHttp\Promise\rejection_for` function instead of object init [#1827](https://github.com/guzzle/guzzle/pull/1827)
+
+
++ Minor code cleanups, documentation fixes and clarifications.
+
+## 6.2.3 - 2017-02-28
+
+* Fix deprecations with guzzle/psr7 version 1.4
+
+## 6.2.2 - 2016-10-08
+
+* Allow to pass nullable Response to delay callable
+* Only add scheme when host is present
+* Fix drain case where content-length is the literal string zero
+* Obfuscate in-URL credentials in exceptions
+
+## 6.2.1 - 2016-07-18
+
+* Address HTTP_PROXY security vulnerability, CVE-2016-5385:
+ https://httpoxy.org/
+* Fixing timeout bug with StreamHandler:
+ https://github.com/guzzle/guzzle/pull/1488
+* Only read up to `Content-Length` in PHP StreamHandler to avoid timeouts when
+ a server does not honor `Connection: close`.
+* Ignore URI fragment when sending requests.
+
+## 6.2.0 - 2016-03-21
+
+* Feature: added `GuzzleHttp\json_encode` and `GuzzleHttp\json_decode`.
+ https://github.com/guzzle/guzzle/pull/1389
+* Bug fix: Fix sleep calculation when waiting for delayed requests.
+ https://github.com/guzzle/guzzle/pull/1324
+* Feature: More flexible history containers.
+ https://github.com/guzzle/guzzle/pull/1373
+* Bug fix: defer sink stream opening in StreamHandler.
+ https://github.com/guzzle/guzzle/pull/1377
+* Bug fix: do not attempt to escape cookie values.
+ https://github.com/guzzle/guzzle/pull/1406
+* Feature: report original content encoding and length on decoded responses.
+ https://github.com/guzzle/guzzle/pull/1409
+* Bug fix: rewind seekable request bodies before dispatching to cURL.
+ https://github.com/guzzle/guzzle/pull/1422
+* Bug fix: provide an empty string to `http_build_query` for HHVM workaround.
+ https://github.com/guzzle/guzzle/pull/1367
+
+## 6.1.1 - 2015-11-22
+
+* Bug fix: Proxy::wrapSync() now correctly proxies to the appropriate handler
+ https://github.com/guzzle/guzzle/commit/911bcbc8b434adce64e223a6d1d14e9a8f63e4e4
+* Feature: HandlerStack is now more generic.
+ https://github.com/guzzle/guzzle/commit/f2102941331cda544745eedd97fc8fd46e1ee33e
+* Bug fix: setting verify to false in the StreamHandler now disables peer
+ verification. https://github.com/guzzle/guzzle/issues/1256
+* Feature: Middleware now uses an exception factory, including more error
+ context. https://github.com/guzzle/guzzle/pull/1282
+* Feature: better support for disabled functions.
+ https://github.com/guzzle/guzzle/pull/1287
+* Bug fix: fixed regression where MockHandler was not using `sink`.
+ https://github.com/guzzle/guzzle/pull/1292
+
+## 6.1.0 - 2015-09-08
+
+* Feature: Added the `on_stats` request option to provide access to transfer
+ statistics for requests. https://github.com/guzzle/guzzle/pull/1202
+* Feature: Added the ability to persist session cookies in CookieJars.
+ https://github.com/guzzle/guzzle/pull/1195
+* Feature: Some compatibility updates for Google APP Engine
+ https://github.com/guzzle/guzzle/pull/1216
+* Feature: Added support for NO_PROXY to prevent the use of a proxy based on
+ a simple set of rules. https://github.com/guzzle/guzzle/pull/1197
+* Feature: Cookies can now contain square brackets.
+ https://github.com/guzzle/guzzle/pull/1237
+* Bug fix: Now correctly parsing `=` inside of quotes in Cookies.
+ https://github.com/guzzle/guzzle/pull/1232
+* Bug fix: Cusotm cURL options now correctly override curl options of the
+ same name. https://github.com/guzzle/guzzle/pull/1221
+* Bug fix: Content-Type header is now added when using an explicitly provided
+ multipart body. https://github.com/guzzle/guzzle/pull/1218
+* Bug fix: Now ignoring Set-Cookie headers that have no name.
+* Bug fix: Reason phrase is no longer cast to an int in some cases in the
+ cURL handler. https://github.com/guzzle/guzzle/pull/1187
+* Bug fix: Remove the Authorization header when redirecting if the Host
+ header changes. https://github.com/guzzle/guzzle/pull/1207
+* Bug fix: Cookie path matching fixes
+ https://github.com/guzzle/guzzle/issues/1129
+* Bug fix: Fixing the cURL `body_as_string` setting
+ https://github.com/guzzle/guzzle/pull/1201
+* Bug fix: quotes are no longer stripped when parsing cookies.
+ https://github.com/guzzle/guzzle/issues/1172
+* Bug fix: `form_params` and `query` now always uses the `&` separator.
+ https://github.com/guzzle/guzzle/pull/1163
+* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
+ https://github.com/guzzle/guzzle/pull/1189
+
+## 6.0.2 - 2015-07-04
+
+* Fixed a memory leak in the curl handlers in which references to callbacks
+ were not being removed by `curl_reset`.
+* Cookies are now extracted properly before redirects.
+* Cookies now allow more character ranges.
+* Decoded Content-Encoding responses are now modified to correctly reflect
+ their state if the encoding was automatically removed by a handler. This
+ means that the `Content-Encoding` header may be removed an the
+ `Content-Length` modified to reflect the message size after removing the
+ encoding.
+* Added a more explicit error message when trying to use `form_params` and
+ `multipart` in the same request.
+* Several fixes for HHVM support.
+* Functions are now conditionally required using an additional level of
+ indirection to help with global Composer installations.
+
+## 6.0.1 - 2015-05-27
+
+* Fixed a bug with serializing the `query` request option where the `&`
+ separator was missing.
+* Added a better error message for when `body` is provided as an array. Please
+ use `form_params` or `multipart` instead.
+* Various doc fixes.
+
+## 6.0.0 - 2015-05-26
+
+* See the UPGRADING.md document for more information.
+* Added `multipart` and `form_params` request options.
+* Added `synchronous` request option.
+* Added the `on_headers` request option.
+* Fixed `expect` handling.
+* No longer adding default middlewares in the client ctor. These need to be
+ present on the provided handler in order to work.
+* Requests are no longer initiated when sending async requests with the
+ CurlMultiHandler. This prevents unexpected recursion from requests completing
+ while ticking the cURL loop.
+* Removed the semantics of setting `default` to `true`. This is no longer
+ required now that the cURL loop is not ticked for async requests.
+* Added request and response logging middleware.
+* No longer allowing self signed certificates when using the StreamHandler.
+* Ensuring that `sink` is valid if saving to a file.
+* Request exceptions now include a "handler context" which provides handler
+ specific contextual information.
+* Added `GuzzleHttp\RequestOptions` to allow request options to be applied
+ using constants.
+* `$maxHandles` has been removed from CurlMultiHandler.
+* `MultipartPostBody` is now part of the `guzzlehttp/psr7` package.
+
+## 5.3.0 - 2015-05-19
+
+* Mock now supports `save_to`
+* Marked `AbstractRequestEvent::getTransaction()` as public.
+* Fixed a bug in which multiple headers using different casing would overwrite
+ previous headers in the associative array.
+* Added `Utils::getDefaultHandler()`
+* Marked `GuzzleHttp\Client::getDefaultUserAgent` as deprecated.
+* URL scheme is now always lowercased.
+
+## 6.0.0-beta.1
+
+* Requires PHP >= 5.5
+* Updated to use PSR-7
+ * Requires immutable messages, which basically means an event based system
+ owned by a request instance is no longer possible.
+ * Utilizing the [Guzzle PSR-7 package](https://github.com/guzzle/psr7).
+ * Removed the dependency on `guzzlehttp/streams`. These stream abstractions
+ are available in the `guzzlehttp/psr7` package under the `GuzzleHttp\Psr7`
+ namespace.
+* Added middleware and handler system
+ * Replaced the Guzzle event and subscriber system with a middleware system.
+ * No longer depends on RingPHP, but rather places the HTTP handlers directly
+ in Guzzle, operating on PSR-7 messages.
+ * Retry logic is now encapsulated in `GuzzleHttp\Middleware::retry`, which
+ means the `guzzlehttp/retry-subscriber` is now obsolete.
+ * Mocking responses is now handled using `GuzzleHttp\Handler\MockHandler`.
+* Asynchronous responses
+ * No longer supports the `future` request option to send an async request.
+ Instead, use one of the `*Async` methods of a client (e.g., `requestAsync`,
+ `getAsync`, etc.).
+ * Utilizing `GuzzleHttp\Promise` instead of React's promise library to avoid
+ recursion required by chaining and forwarding react promises. See
+ https://github.com/guzzle/promises
+ * Added `requestAsync` and `sendAsync` to send request asynchronously.
+ * Added magic methods for `getAsync()`, `postAsync()`, etc. to send requests
+ asynchronously.
+* Request options
+ * POST and form updates
+ * Added the `form_fields` and `form_files` request options.
+ * Removed the `GuzzleHttp\Post` namespace.
+ * The `body` request option no longer accepts an array for POST requests.
+ * The `exceptions` request option has been deprecated in favor of the
+ `http_errors` request options.
+ * The `save_to` request option has been deprecated in favor of `sink` request
+ option.
+* Clients no longer accept an array of URI template string and variables for
+ URI variables. You will need to expand URI templates before passing them
+ into a client constructor or request method.
+* Client methods `get()`, `post()`, `put()`, `patch()`, `options()`, etc. are
+ now magic methods that will send synchronous requests.
+* Replaced `Utils.php` with plain functions in `functions.php`.
+* Removed `GuzzleHttp\Collection`.
+* Removed `GuzzleHttp\BatchResults`. Batched pool results are now returned as
+ an array.
+* Removed `GuzzleHttp\Query`. Query string handling is now handled using an
+ associative array passed into the `query` request option. The query string
+ is serialized using PHP's `http_build_query`. If you need more control, you
+ can pass the query string in as a string.
+* `GuzzleHttp\QueryParser` has been replaced with the
+ `GuzzleHttp\Psr7\parse_query`.
+
+## 5.2.0 - 2015-01-27
+
+* Added `AppliesHeadersInterface` to make applying headers to a request based
+ on the body more generic and not specific to `PostBodyInterface`.
+* Reduced the number of stack frames needed to send requests.
+* Nested futures are now resolved in the client rather than the RequestFsm
+* Finishing state transitions is now handled in the RequestFsm rather than the
+ RingBridge.
+* Added a guard in the Pool class to not use recursion for request retries.
+
+## 5.1.0 - 2014-12-19
+
+* Pool class no longer uses recursion when a request is intercepted.
+* The size of a Pool can now be dynamically adjusted using a callback.
+ See https://github.com/guzzle/guzzle/pull/943.
+* Setting a request option to `null` when creating a request with a client will
+ ensure that the option is not set. This allows you to overwrite default
+ request options on a per-request basis.
+ See https://github.com/guzzle/guzzle/pull/937.
+* Added the ability to limit which protocols are allowed for redirects by
+ specifying a `protocols` array in the `allow_redirects` request option.
+* Nested futures due to retries are now resolved when waiting for synchronous
+ responses. See https://github.com/guzzle/guzzle/pull/947.
+* `"0"` is now an allowed URI path. See
+ https://github.com/guzzle/guzzle/pull/935.
+* `Query` no longer typehints on the `$query` argument in the constructor,
+ allowing for strings and arrays.
+* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
+ specific exceptions if necessary.
+
+## 5.0.3 - 2014-11-03
+
+This change updates query strings so that they are treated as un-encoded values
+by default where the value represents an un-encoded value to send over the
+wire. A Query object then encodes the value before sending over the wire. This
+means that even value query string values (e.g., ":") are url encoded. This
+makes the Query class match PHP's http_build_query function. However, if you
+want to send requests over the wire using valid query string characters that do
+not need to be encoded, then you can provide a string to Url::setQuery() and
+pass true as the second argument to specify that the query string is a raw
+string that should not be parsed or encoded (unless a call to getQuery() is
+subsequently made, forcing the query-string to be converted into a Query
+object).
+
+## 5.0.2 - 2014-10-30
+
+* Added a trailing `\r\n` to multipart/form-data payloads. See
+ https://github.com/guzzle/guzzle/pull/871
+* Added a `GuzzleHttp\Pool::send()` convenience method to match the docs.
+* Status codes are now returned as integers. See
+ https://github.com/guzzle/guzzle/issues/881
+* No longer overwriting an existing `application/x-www-form-urlencoded` header
+ when sending POST requests, allowing for customized headers. See
+ https://github.com/guzzle/guzzle/issues/877
+* Improved path URL serialization.
+
+ * No longer double percent-encoding characters in the path or query string if
+ they are already encoded.
+ * Now properly encoding the supplied path to a URL object, instead of only
+ encoding ' ' and '?'.
+ * Note: This has been changed in 5.0.3 to now encode query string values by
+ default unless the `rawString` argument is provided when setting the query
+ string on a URL: Now allowing many more characters to be present in the
+ query string without being percent encoded. See http://tools.ietf.org/html/rfc3986#appendix-A
+
+## 5.0.1 - 2014-10-16
+
+Bugfix release.
+
+* Fixed an issue where connection errors still returned response object in
+ error and end events event though the response is unusable. This has been
+ corrected so that a response is not returned in the `getResponse` method of
+ these events if the response did not complete. https://github.com/guzzle/guzzle/issues/867
+* Fixed an issue where transfer statistics were not being populated in the
+ RingBridge. https://github.com/guzzle/guzzle/issues/866
+
+## 5.0.0 - 2014-10-12
+
+Adding support for non-blocking responses and some minor API cleanup.
+
+### New Features
+
+* Added support for non-blocking responses based on `guzzlehttp/guzzle-ring`.
+* Added a public API for creating a default HTTP adapter.
+* Updated the redirect plugin to be non-blocking so that redirects are sent
+ concurrently. Other plugins like this can now be updated to be non-blocking.
+* Added a "progress" event so that you can get upload and download progress
+ events.
+* Added `GuzzleHttp\Pool` which implements FutureInterface and transfers
+ requests concurrently using a capped pool size as efficiently as possible.
+* Added `hasListeners()` to EmitterInterface.
+* Removed `GuzzleHttp\ClientInterface::sendAll` and marked
+ `GuzzleHttp\Client::sendAll` as deprecated (it's still there, just not the
+ recommended way).
+
+### Breaking changes
+
+The breaking changes in this release are relatively minor. The biggest thing to
+look out for is that request and response objects no longer implement fluent
+interfaces.
+
+* Removed the fluent interfaces (i.e., `return $this`) from requests,
+ responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`,
+ `GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and
+ `GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of
+ why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/.
+ This also makes the Guzzle message interfaces compatible with the current
+ PSR-7 message proposal.
+* Removed "functions.php", so that Guzzle is truly PSR-4 compliant. Except
+ for the HTTP request functions from function.php, these functions are now
+ implemented in `GuzzleHttp\Utils` using camelCase. `GuzzleHttp\json_decode`
+ moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to
+ `GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to
+ `GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be
+ `GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php
+ caused problems for many users: they aren't PSR-4 compliant, require an
+ explicit include, and needed an if-guard to ensure that the functions are not
+ declared multiple times.
+* Rewrote adapter layer.
+ * Removing all classes from `GuzzleHttp\Adapter`, these are now
+ implemented as callables that are stored in `GuzzleHttp\Ring\Client`.
+ * Removed the concept of "parallel adapters". Sending requests serially or
+ concurrently is now handled using a single adapter.
+ * Moved `GuzzleHttp\Adapter\Transaction` to `GuzzleHttp\Transaction`. The
+ Transaction object now exposes the request, response, and client as public
+ properties. The getters and setters have been removed.
+* Removed the "headers" event. This event was only useful for changing the
+ body a response once the headers of the response were known. You can implement
+ a similar behavior in a number of ways. One example might be to use a
+ FnStream that has access to the transaction being sent. For example, when the
+ first byte is written, you could check if the response headers match your
+ expectations, and if so, change the actual stream body that is being
+ written to.
+* Removed the `asArray` parameter from
+ `GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+ value as an array, then use the newly added `getHeaderAsArray()` method of
+ `MessageInterface`. This change makes the Guzzle interfaces compatible with
+ the PSR-7 interfaces.
+* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add
+ custom request options using double-dispatch (this was an implementation
+ detail). Instead, you should now provide an associative array to the
+ constructor which is a mapping of the request option name mapping to a
+ function that applies the option value to a request.
+* Removed the concept of "throwImmediately" from exceptions and error events.
+ This control mechanism was used to stop a transfer of concurrent requests
+ from completing. This can now be handled by throwing the exception or by
+ cancelling a pool of requests or each outstanding future request individually.
+* Updated to "GuzzleHttp\Streams" 3.0.
+ * `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a
+ `maxLen` parameter. This update makes the Guzzle streams project
+ compatible with the current PSR-7 proposal.
+ * `GuzzleHttp\Stream\Stream::__construct`,
+ `GuzzleHttp\Stream\Stream::factory`, and
+ `GuzzleHttp\Stream\Utils::create` no longer accept a size in the second
+ argument. They now accept an associative array of options, including the
+ "size" key and "metadata" key which can be used to provide custom metadata.
+
+## 4.2.2 - 2014-09-08
+
+* Fixed a memory leak in the CurlAdapter when reusing cURL handles.
+* No longer using `request_fulluri` in stream adapter proxies.
+* Relative redirects are now based on the last response, not the first response.
+
+## 4.2.1 - 2014-08-19
+
+* Ensuring that the StreamAdapter does not always add a Content-Type header
+* Adding automated github releases with a phar and zip
+
+## 4.2.0 - 2014-08-17
+
+* Now merging in default options using a case-insensitive comparison.
+ Closes https://github.com/guzzle/guzzle/issues/767
+* Added the ability to automatically decode `Content-Encoding` response bodies
+ using the `decode_content` request option. This is set to `true` by default
+ to decode the response body if it comes over the wire with a
+ `Content-Encoding`. Set this value to `false` to disable decoding the
+ response content, and pass a string to provide a request `Accept-Encoding`
+ header and turn on automatic response decoding. This feature now allows you
+ to pass an `Accept-Encoding` header in the headers of a request but still
+ disable automatic response decoding.
+ Closes https://github.com/guzzle/guzzle/issues/764
+* Added the ability to throw an exception immediately when transferring
+ requests in parallel. Closes https://github.com/guzzle/guzzle/issues/760
+* Updating guzzlehttp/streams dependency to ~2.1
+* No longer utilizing the now deprecated namespaced methods from the stream
+ package.
+
+## 4.1.8 - 2014-08-14
+
+* Fixed an issue in the CurlFactory that caused setting the `stream=false`
+ request option to throw an exception.
+ See: https://github.com/guzzle/guzzle/issues/769
+* TransactionIterator now calls rewind on the inner iterator.
+ See: https://github.com/guzzle/guzzle/pull/765
+* You can now set the `Content-Type` header to `multipart/form-data`
+ when creating POST requests to force multipart bodies.
+ See https://github.com/guzzle/guzzle/issues/768
+
+## 4.1.7 - 2014-08-07
+
+* Fixed an error in the HistoryPlugin that caused the same request and response
+ to be logged multiple times when an HTTP protocol error occurs.
+* Ensuring that cURL does not add a default Content-Type when no Content-Type
+ has been supplied by the user. This prevents the adapter layer from modifying
+ the request that is sent over the wire after any listeners may have already
+ put the request in a desired state (e.g., signed the request).
+* Throwing an exception when you attempt to send requests that have the
+ "stream" set to true in parallel using the MultiAdapter.
+* Only calling curl_multi_select when there are active cURL handles. This was
+ previously changed and caused performance problems on some systems due to PHP
+ always selecting until the maximum select timeout.
+* Fixed a bug where multipart/form-data POST fields were not correctly
+ aggregated (e.g., values with "&").
+
+## 4.1.6 - 2014-08-03
+
+* Added helper methods to make it easier to represent messages as strings,
+ including getting the start line and getting headers as a string.
+
+## 4.1.5 - 2014-08-02
+
+* Automatically retrying cURL "Connection died, retrying a fresh connect"
+ errors when possible.
+* cURL implementation cleanup
+* Allowing multiple event subscriber listeners to be registered per event by
+ passing an array of arrays of listener configuration.
+
+## 4.1.4 - 2014-07-22
+
+* Fixed a bug that caused multi-part POST requests with more than one field to
+ serialize incorrectly.
+* Paths can now be set to "0"
+* `ResponseInterface::xml` now accepts a `libxml_options` option and added a
+ missing default argument that was required when parsing XML response bodies.
+* A `save_to` stream is now created lazily, which means that files are not
+ created on disk unless a request succeeds.
+
+## 4.1.3 - 2014-07-15
+
+* Various fixes to multipart/form-data POST uploads
+* Wrapping function.php in an if-statement to ensure Guzzle can be used
+ globally and in a Composer install
+* Fixed an issue with generating and merging in events to an event array
+* POST headers are only applied before sending a request to allow you to change
+ the query aggregator used before uploading
+* Added much more robust query string parsing
+* Fixed various parsing and normalization issues with URLs
+* Fixing an issue where multi-valued headers were not being utilized correctly
+ in the StreamAdapter
+
+## 4.1.2 - 2014-06-18
+
+* Added support for sending payloads with GET requests
+
+## 4.1.1 - 2014-06-08
+
+* Fixed an issue related to using custom message factory options in subclasses
+* Fixed an issue with nested form fields in a multi-part POST
+* Fixed an issue with using the `json` request option for POST requests
+* Added `ToArrayInterface` to `GuzzleHttp\Cookie\CookieJar`
+
+## 4.1.0 - 2014-05-27
+
+* Added a `json` request option to easily serialize JSON payloads.
+* Added a `GuzzleHttp\json_decode()` wrapper to safely parse JSON.
+* Added `setPort()` and `getPort()` to `GuzzleHttp\Message\RequestInterface`.
+* Added the ability to provide an emitter to a client in the client constructor.
+* Added the ability to persist a cookie session using $_SESSION.
+* Added a trait that can be used to add event listeners to an iterator.
+* Removed request method constants from RequestInterface.
+* Fixed warning when invalid request start-lines are received.
+* Updated MessageFactory to work with custom request option methods.
+* Updated cacert bundle to latest build.
+
+4.0.2 (2014-04-16)
+------------------
+
+* Proxy requests using the StreamAdapter now properly use request_fulluri (#632)
+* Added the ability to set scalars as POST fields (#628)
+
+## 4.0.1 - 2014-04-04
+
+* The HTTP status code of a response is now set as the exception code of
+ RequestException objects.
+* 303 redirects will now correctly switch from POST to GET requests.
+* The default parallel adapter of a client now correctly uses the MultiAdapter.
+* HasDataTrait now initializes the internal data array as an empty array so
+ that the toArray() method always returns an array.
+
+## 4.0.0 - 2014-03-29
+
+* For more information on the 4.0 transition, see:
+ http://mtdowling.com/blog/2014/03/15/guzzle-4-rc/
+* For information on changes and upgrading, see:
+ https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
+* Added `GuzzleHttp\batch()` as a convenience function for sending requests in
+ parallel without needing to write asynchronous code.
+* Restructured how events are added to `GuzzleHttp\ClientInterface::sendAll()`.
+ You can now pass a callable or an array of associative arrays where each
+ associative array contains the "fn", "priority", and "once" keys.
+
+## 4.0.0.rc-2 - 2014-03-25
+
+* Removed `getConfig()` and `setConfig()` from clients to avoid confusion
+ around whether things like base_url, message_factory, etc. should be able to
+ be retrieved or modified.
+* Added `getDefaultOption()` and `setDefaultOption()` to ClientInterface
+* functions.php functions were renamed using snake_case to match PHP idioms
+* Added support for `HTTP_PROXY`, `HTTPS_PROXY`, and
+ `GUZZLE_CURL_SELECT_TIMEOUT` environment variables
+* Added the ability to specify custom `sendAll()` event priorities
+* Added the ability to specify custom stream context options to the stream
+ adapter.
+* Added a functions.php function for `get_path()` and `set_path()`
+* CurlAdapter and MultiAdapter now use a callable to generate curl resources
+* MockAdapter now properly reads a body and emits a `headers` event
+* Updated Url class to check if a scheme and host are set before adding ":"
+ and "//". This allows empty Url (e.g., "") to be serialized as "".
+* Parsing invalid XML no longer emits warnings
+* Curl classes now properly throw AdapterExceptions
+* Various performance optimizations
+* Streams are created with the faster `Stream\create()` function
+* Marked deprecation_proxy() as internal
+* Test server is now a collection of static methods on a class
+
+## 4.0.0-rc.1 - 2014-03-15
+
+* See https://github.com/guzzle/guzzle/blob/master/UPGRADING.md#3x-to-40
+
+## 3.8.1 - 2014-01-28
+
+* Bug: Always using GET requests when redirecting from a 303 response
+* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in
+ `Guzzle\Http\ClientInterface::setSslVerification()`
+* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL
+* Bug: The body of a request can now be set to `"0"`
+* Sending PHP stream requests no longer forces `HTTP/1.0`
+* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of
+ each sub-exception
+* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than
+ clobbering everything).
+* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators)
+* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`.
+ For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`.
+* Now properly escaping the regular expression delimiter when matching Cookie domains.
+* Network access is now disabled when loading XML documents
+
+## 3.8.0 - 2013-12-05
+
+* Added the ability to define a POST name for a file
+* JSON response parsing now properly walks additionalProperties
+* cURL error code 18 is now retried automatically in the BackoffPlugin
+* Fixed a cURL error when URLs contain fragments
+* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were
+ CurlExceptions
+* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e)
+* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS`
+* Fixed a bug that was encountered when parsing empty header parameters
+* UriTemplate now has a `setRegex()` method to match the docs
+* The `debug` request parameter now checks if it is truthy rather than if it exists
+* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin
+* Added the ability to combine URLs using strict RFC 3986 compliance
+* Command objects can now return the validation errors encountered by the command
+* Various fixes to cache revalidation (#437 and 29797e5)
+* Various fixes to the AsyncPlugin
+* Cleaned up build scripts
+
+## 3.7.4 - 2013-10-02
+
+* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430)
+* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp
+ (see https://github.com/aws/aws-sdk-php/issues/147)
+* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots
+* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420)
+* Updated the bundled cacert.pem (#419)
+* OauthPlugin now supports adding authentication to headers or query string (#425)
+
+## 3.7.3 - 2013-09-08
+
+* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and
+ `CommandTransferException`.
+* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description
+* Schemas are only injected into response models when explicitly configured.
+* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of
+ an EntityBody.
+* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator.
+* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
+* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
+* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
+* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests
+* Bug fix: Properly parsing headers that contain commas contained in quotes
+* Bug fix: mimetype guessing based on a filename is now case-insensitive
+
+## 3.7.2 - 2013-08-02
+
+* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander
+ See https://github.com/guzzle/guzzle/issues/371
+* Bug fix: Cookie domains are now matched correctly according to RFC 6265
+ See https://github.com/guzzle/guzzle/issues/377
+* Bug fix: GET parameters are now used when calculating an OAuth signature
+* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted
+* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched
+* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input.
+ See https://github.com/guzzle/guzzle/issues/379
+* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See
+ https://github.com/guzzle/guzzle/pull/380
+* cURL multi cleanup and optimizations
+
+## 3.7.1 - 2013-07-05
+
+* Bug fix: Setting default options on a client now works
+* Bug fix: Setting options on HEAD requests now works. See #352
+* Bug fix: Moving stream factory before send event to before building the stream. See #353
+* Bug fix: Cookies no longer match on IP addresses per RFC 6265
+* Bug fix: Correctly parsing header parameters that are in `<>` and quotes
+* Added `cert` and `ssl_key` as request options
+* `Host` header can now diverge from the host part of a URL if the header is set manually
+* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter
+* OAuth parameters are only added via the plugin if they aren't already set
+* Exceptions are now thrown when a URL cannot be parsed
+* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails
+* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin
+
+## 3.7.0 - 2013-06-10
+
+* See UPGRADING.md for more information on how to upgrade.
+* Requests now support the ability to specify an array of $options when creating a request to more easily modify a
+ request. You can pass a 'request.options' configuration setting to a client to apply default request options to
+ every request created by a client (e.g. default query string variables, headers, curl options, etc.).
+* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`.
+ See `Guzzle\Http\StaticClient::mount`.
+* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests
+ created by a command (e.g. custom headers, query string variables, timeout settings, etc.).
+* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the
+ headers of a response
+* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key
+ (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`)
+* ServiceBuilders now support storing and retrieving arbitrary data
+* CachePlugin can now purge all resources for a given URI
+* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource
+* CachePlugin now uses the Vary header to determine if a resource is a cache hit
+* `Guzzle\Http\Message\Response` now implements `\Serializable`
+* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters
+* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable
+* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()`
+* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size
+* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message
+* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older
+ Symfony users can still use the old version of Monolog.
+* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`.
+ Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`.
+* Several performance improvements to `Guzzle\Common\Collection`
+* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+ createRequest, head, delete, put, patch, post, options, prepareRequest
+* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+ `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+ resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+ default `array()`
+* Added `Guzzle\Stream\StreamInterface::isRepeatable`
+* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+ $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+ $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`.
+* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`.
+* Removed `Guzzle\Http\ClientInterface::expandTemplate()`
+* Removed `Guzzle\Http\ClientInterface::setRequestFactory()`
+* Removed `Guzzle\Http\ClientInterface::getCurlMulti()`
+* Removed `Guzzle\Http\Message\RequestInterface::canCache`
+* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`
+* Removed `Guzzle\Http\Message\RequestInterface::isRedirect`
+* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting
+ `Guzzle\Common\Version::$emitWarnings` to true.
+* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use
+ `$request->getResponseBody()->isRepeatable()` instead.
+* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
+ `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use
+ `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand.
+ These will work through Guzzle 4.0
+* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params].
+* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`.
+* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`.
+* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+* Marked `Guzzle\Common\Collection::inject()` as deprecated.
+* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');`
+* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+ CacheStorageInterface. These two objects and interface will be removed in a future version.
+* Always setting X-cache headers on cached responses
+* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+ $request, Response $response);`
+* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+* Added `CacheStorageInterface::purge($url)`
+* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+ $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+ CanCacheStrategyInterface $canCache = null)`
+* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+## 3.6.0 - 2013-05-29
+
+* ServiceDescription now implements ToArrayInterface
+* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters
+* Guzzle can now correctly parse incomplete URLs
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+ HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+ CacheControl header implementation.
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+ Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+ directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+ but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+ `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+ on a request while the request is still being transferred
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+ instead.
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+* Added the ability to cast Model objects to a string to view debug information.
+
+## 3.5.0 - 2013-05-13
+
+* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
+* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove
+ itself from the EventDispatcher)
+* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
+* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
+* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a
+ non-existent key
+* Bug: All __call() method arguments are now required (helps with mocking frameworks)
+* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference
+ to help with refcount based garbage collection of resources created by sending a request
+* Deprecating ZF1 cache and log adapters. These will be removed in the next major version.
+* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it's deprecated). Use the
+ HistoryPlugin for a history.
+* Added a `responseBody` alias for the `response_body` location
+* Refactored internals to no longer rely on Response::getRequest()
+* HistoryPlugin can now be cast to a string
+* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests
+ and responses that are sent over the wire
+* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects
+
+## 3.4.3 - 2013-04-30
+
+* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response
+* Added a check to re-extract the temp cacert bundle from the phar before sending each request
+
+## 3.4.2 - 2013-04-29
+
+* Bug fix: Stream objects now work correctly with "a" and "a+" modes
+* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present
+* Bug fix: AsyncPlugin no longer forces HEAD requests
+* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter
+* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails
+* Setting a response on a request will write to the custom request body from the response body if one is specified
+* LogPlugin now writes to php://output when STDERR is undefined
+* Added the ability to set multiple POST files for the same key in a single call
+* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default
+* Added the ability to queue CurlExceptions to the MockPlugin
+* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send)
+* Configuration loading now allows remote files
+
+## 3.4.1 - 2013-04-16
+
+* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti
+ handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost.
+* Exceptions are now properly grouped when sending requests in parallel
+* Redirects are now properly aggregated when a multi transaction fails
+* Redirects now set the response on the original object even in the event of a failure
+* Bug fix: Model names are now properly set even when using $refs
+* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax
+* Added support for oauth_callback in OAuth signatures
+* Added support for oauth_verifier in OAuth signatures
+* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection
+
+## 3.4.0 - 2013-04-11
+
+* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289
+* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289
+* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263
+* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264.
+* Bug fix: Added `number` type to service descriptions.
+* Bug fix: empty parameters are removed from an OAuth signature
+* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header
+* Bug fix: Fixed "array to string" error when validating a union of types in a service description
+* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream
+* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin.
+* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs.
+* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections.
+* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if
+ the Content-Type can be determined based on the entity body or the path of the request.
+* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder.
+* Added support for a PSR-3 LogAdapter.
+* Added a `command.after_prepare` event
+* Added `oauth_callback` parameter to the OauthPlugin
+* Added the ability to create a custom stream class when using a stream factory
+* Added a CachingEntityBody decorator
+* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized.
+* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar.
+* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies
+* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This
+ means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use
+ POST fields or files (the latter is only used when emulating a form POST in the browser).
+* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest
+
+## 3.3.1 - 2013-03-10
+
+* Added the ability to create PHP streaming responses from HTTP requests
+* Bug fix: Running any filters when parsing response headers with service descriptions
+* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing
+* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across
+ response location visitors.
+* Bug fix: Removed the possibility of creating configuration files with circular dependencies
+* RequestFactory::create() now uses the key of a POST file when setting the POST file name
+* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set
+
+## 3.3.0 - 2013-03-03
+
+* A large number of performance optimizations have been made
+* Bug fix: Added 'wb' as a valid write mode for streams
+* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned
+* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()`
+* BC: Removed `Guzzle\Http\Utils` class
+* BC: Setting a service description on a client will no longer modify the client's command factories.
+* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using
+ the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to
+ lowercase
+* Operation parameter objects are now lazy loaded internally
+* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses
+* Added support for instantiating responseType=class responseClass classes. Classes must implement
+ `Guzzle\Service\Command\ResponseClassInterface`
+* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These
+ additional properties also support locations and can be used to parse JSON responses where the outermost part of the
+ JSON is an array
+* Added support for nested renaming of JSON models (rename sentAs to name)
+* CachePlugin
+ * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error
+ * Debug headers can now added to cached response in the CachePlugin
+
+## 3.2.0 - 2013-02-14
+
+* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients.
+* URLs with no path no longer contain a "/" by default
+* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url.
+* BadResponseException no longer includes the full request and response message
+* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface
+* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface
+* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription
+* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list
+* xmlEncoding can now be customized for the XML declaration of a XML service description operation
+* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value
+ aggregation and no longer uses callbacks
+* The URL encoding implementation of Guzzle\Http\QueryString can now be customized
+* Bug fix: Filters were not always invoked for array service description parameters
+* Bug fix: Redirects now use a target response body rather than a temporary response body
+* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded
+* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives
+
+## 3.1.2 - 2013-01-27
+
+* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the
+ response body. For example, the XmlVisitor now parses the XML response into an array in the before() method.
+* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent
+* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444)
+* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse()
+* Setting default headers on a client after setting the user-agent will not erase the user-agent setting
+
+## 3.1.1 - 2013-01-20
+
+* Adding wildcard support to Guzzle\Common\Collection::getPath()
+* Adding alias support to ServiceBuilder configs
+* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface
+
+## 3.1.0 - 2013-01-12
+
+* BC: CurlException now extends from RequestException rather than BadResponseException
+* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse()
+* Added getData to ServiceDescriptionInterface
+* Added context array to RequestInterface::setState()
+* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http
+* Bug: Adding required content-type when JSON request visitor adds JSON to a command
+* Bug: Fixing the serialization of a service description with custom data
+* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing
+ an array of successful and failed responses
+* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection
+* Added Guzzle\Http\IoEmittingEntityBody
+* Moved command filtration from validators to location visitors
+* Added `extends` attributes to service description parameters
+* Added getModels to ServiceDescriptionInterface
+
+## 3.0.7 - 2012-12-19
+
+* Fixing phar detection when forcing a cacert to system if null or true
+* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()`
+* Cleaning up `Guzzle\Common\Collection::inject` method
+* Adding a response_body location to service descriptions
+
+## 3.0.6 - 2012-12-09
+
+* CurlMulti performance improvements
+* Adding setErrorResponses() to Operation
+* composer.json tweaks
+
+## 3.0.5 - 2012-11-18
+
+* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin
+* Bug: Response body can now be a string containing "0"
+* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert
+* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs
+* Added support for XML attributes in service description responses
+* DefaultRequestSerializer now supports array URI parameter values for URI template expansion
+* Added better mimetype guessing to requests and post files
+
+## 3.0.4 - 2012-11-11
+
+* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value
+* Bug: Cookies can now be added that have a name, domain, or value set to "0"
+* Bug: Using the system cacert bundle when using the Phar
+* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures
+* Enhanced cookie jar de-duplication
+* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added
+* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies
+* Added the ability to create any sort of hash for a stream rather than just an MD5 hash
+
+## 3.0.3 - 2012-11-04
+
+* Implementing redirects in PHP rather than cURL
+* Added PECL URI template extension and using as default parser if available
+* Bug: Fixed Content-Length parsing of Response factory
+* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams.
+* Adding ToArrayInterface throughout library
+* Fixing OauthPlugin to create unique nonce values per request
+
+## 3.0.2 - 2012-10-25
+
+* Magic methods are enabled by default on clients
+* Magic methods return the result of a command
+* Service clients no longer require a base_url option in the factory
+* Bug: Fixed an issue with URI templates where null template variables were being expanded
+
+## 3.0.1 - 2012-10-22
+
+* Models can now be used like regular collection objects by calling filter, map, etc.
+* Models no longer require a Parameter structure or initial data in the constructor
+* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator`
+
+## 3.0.0 - 2012-10-15
+
+* Rewrote service description format to be based on Swagger
+ * Now based on JSON schema
+ * Added nested input structures and nested response models
+ * Support for JSON and XML input and output models
+ * Renamed `commands` to `operations`
+ * Removed dot class notation
+ * Removed custom types
+* Broke the project into smaller top-level namespaces to be more component friendly
+* Removed support for XML configs and descriptions. Use arrays or JSON files.
+* Removed the Validation component and Inspector
+* Moved all cookie code to Guzzle\Plugin\Cookie
+* Magic methods on a Guzzle\Service\Client now return the command un-executed.
+* Calling getResult() or getResponse() on a command will lazily execute the command if needed.
+* Now shipping with cURL's CA certs and using it by default
+* Added previousResponse() method to response objects
+* No longer sending Accept and Accept-Encoding headers on every request
+* Only sending an Expect header by default when a payload is greater than 1MB
+* Added/moved client options:
+ * curl.blacklist to curl.option.blacklist
+ * Added ssl.certificate_authority
+* Added a Guzzle\Iterator component
+* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin
+* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin)
+* Added a more robust caching plugin
+* Added setBody to response objects
+* Updating LogPlugin to use a more flexible MessageFormatter
+* Added a completely revamped build process
+* Cleaning up Collection class and removing default values from the get method
+* Fixed ZF2 cache adapters
+
+## 2.8.8 - 2012-10-15
+
+* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did
+
+## 2.8.7 - 2012-09-30
+
+* Bug: Fixed config file aliases for JSON includes
+* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests
+* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload
+* Bug: Hardening request and response parsing to account for missing parts
+* Bug: Fixed PEAR packaging
+* Bug: Fixed Request::getInfo
+* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail
+* Adding the ability for the namespace Iterator factory to look in multiple directories
+* Added more getters/setters/removers from service descriptions
+* Added the ability to remove POST fields from OAuth signatures
+* OAuth plugin now supports 2-legged OAuth
+
+## 2.8.6 - 2012-09-05
+
+* Added the ability to modify and build service descriptions
+* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command
+* Added a `json` parameter location
+* Now allowing dot notation for classes in the CacheAdapterFactory
+* Using the union of two arrays rather than an array_merge when extending service builder services and service params
+* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references
+ in service builder config files.
+* Services defined in two different config files that include one another will by default replace the previously
+ defined service, but you can now create services that extend themselves and merge their settings over the previous
+* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like
+ '_default' with a default JSON configuration file.
+
+## 2.8.5 - 2012-08-29
+
+* Bug: Suppressed empty arrays from URI templates
+* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching
+* Added support for HTTP responses that do not contain a reason phrase in the start-line
+* AbstractCommand commands are now invokable
+* Added a way to get the data used when signing an Oauth request before a request is sent
+
+## 2.8.4 - 2012-08-15
+
+* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin
+* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable.
+* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream
+* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream
+* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5())
+* Added additional response status codes
+* Removed SSL information from the default User-Agent header
+* DELETE requests can now send an entity body
+* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries
+* Added the ability of the MockPlugin to consume mocked request bodies
+* LogPlugin now exposes request and response objects in the extras array
+
+## 2.8.3 - 2012-07-30
+
+* Bug: Fixed a case where empty POST requests were sent as GET requests
+* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
+* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
+* Added multiple inheritance to service description commands
+* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()`
+* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
+* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles
+
+## 2.8.2 - 2012-07-24
+
+* Bug: Query string values set to 0 are no longer dropped from the query string
+* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`
+* Bug: `+` is now treated as an encoded space when parsing query strings
+* QueryString and Collection performance improvements
+* Allowing dot notation for class paths in filters attribute of a service descriptions
+
+## 2.8.1 - 2012-07-16
+
+* Loosening Event Dispatcher dependency
+* POST redirects can now be customized using CURLOPT_POSTREDIR
+
+## 2.8.0 - 2012-07-15
+
+* BC: Guzzle\Http\Query
+ * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl)
+ * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding()
+ * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
+ * Changed the aggregation functions of QueryString to be static methods
+ * Can now use fromString() with querystrings that have a leading ?
+* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters
+* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
+* Cookies are no longer URL decoded by default
+* Bug: URI template variables set to null are no longer expanded
+
+## 2.7.2 - 2012-07-02
+
+* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser.
+* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty()
+* CachePlugin now allows for a custom request parameter function to check if a request can be cached
+* Bug fix: CachePlugin now only caches GET and HEAD requests by default
+* Bug fix: Using header glue when transferring headers over the wire
+* Allowing deeply nested arrays for composite variables in URI templates
+* Batch divisors can now return iterators or arrays
+
+## 2.7.1 - 2012-06-26
+
+* Minor patch to update version number in UA string
+* Updating build process
+
+## 2.7.0 - 2012-06-25
+
+* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes.
+* BC: Removed magic setX methods from commands
+* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method
+* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable.
+* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity)
+* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace
+* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin
+* Added the ability to set POST fields and files in a service description
+* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method
+* Adding a command.before_prepare event to clients
+* Added BatchClosureTransfer and BatchClosureDivisor
+* BatchTransferException now includes references to the batch divisor and transfer strategies
+* Fixed some tests so that they pass more reliably
+* Added Guzzle\Common\Log\ArrayLogAdapter
+
+## 2.6.6 - 2012-06-10
+
+* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin
+* BC: Removing Guzzle\Service\Command\CommandSet
+* Adding generic batching system (replaces the batch queue plugin and command set)
+* Updating ZF cache and log adapters and now using ZF's composer repository
+* Bug: Setting the name of each ApiParam when creating through an ApiCommand
+* Adding result_type, result_doc, deprecated, and doc_url to service descriptions
+* Bug: Changed the default cookie header casing back to 'Cookie'
+
+## 2.6.5 - 2012-06-03
+
+* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource()
+* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from
+* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data
+* BC: Renaming methods in the CookieJarInterface
+* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations
+* Making the default glue for HTTP headers ';' instead of ','
+* Adding a removeValue to Guzzle\Http\Message\Header
+* Adding getCookies() to request interface.
+* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber()
+
+## 2.6.4 - 2012-05-30
+
+* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class.
+* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand
+* Bug: Fixing magic method command calls on clients
+* Bug: Email constraint only validates strings
+* Bug: Aggregate POST fields when POST files are present in curl handle
+* Bug: Fixing default User-Agent header
+* Bug: Only appending or prepending parameters in commands if they are specified
+* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes
+* Allowing the use of dot notation for class namespaces when using instance_of constraint
+* Added any_match validation constraint
+* Added an AsyncPlugin
+* Passing request object to the calculateWait method of the ExponentialBackoffPlugin
+* Allowing the result of a command object to be changed
+* Parsing location and type sub values when instantiating a service description rather than over and over at runtime
+
+## 2.6.3 - 2012-05-23
+
+* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options.
+* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields.
+* You can now use an array of data when creating PUT request bodies in the request factory.
+* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable.
+* [Http] Adding support for Content-Type in multipart POST uploads per upload
+* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1])
+* Adding more POST data operations for easier manipulation of POST data.
+* You can now set empty POST fields.
+* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files.
+* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate.
+* CS updates
+
+## 2.6.2 - 2012-05-19
+
+* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method.
+
+## 2.6.1 - 2012-05-19
+
+* [BC] Removing 'path' support in service descriptions. Use 'uri'.
+* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache.
+* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it.
+* [BC] Removing Guzzle\Common\XmlElement.
+* All commands, both dynamic and concrete, have ApiCommand objects.
+* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits.
+* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored.
+* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible.
+
+## 2.6.0 - 2012-05-15
+
+* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder
+* [BC] Executing a Command returns the result of the command rather than the command
+* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed.
+* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args.
+* [BC] Moving ResourceIterator* to Guzzle\Service\Resource
+* [BC] Completely refactored ResourceIterators to iterate over a cloned command object
+* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate
+* [BC] Guzzle\Guzzle is now deprecated
+* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject
+* Adding Guzzle\Version class to give version information about Guzzle
+* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate()
+* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data
+* ServiceDescription and ServiceBuilder are now cacheable using similar configs
+* Changing the format of XML and JSON service builder configs. Backwards compatible.
+* Cleaned up Cookie parsing
+* Trimming the default Guzzle User-Agent header
+* Adding a setOnComplete() method to Commands that is called when a command completes
+* Keeping track of requests that were mocked in the MockPlugin
+* Fixed a caching bug in the CacheAdapterFactory
+* Inspector objects can be injected into a Command object
+* Refactoring a lot of code and tests to be case insensitive when dealing with headers
+* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL
+* Adding the ability to set global option overrides to service builder configs
+* Adding the ability to include other service builder config files from within XML and JSON files
+* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method.
+
+## 2.5.0 - 2012-05-08
+
+* Major performance improvements
+* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated.
+* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component.
+* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}"
+* Added the ability to passed parameters to all requests created by a client
+* Added callback functionality to the ExponentialBackoffPlugin
+* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies.
+* Rewinding request stream bodies when retrying requests
+* Exception is thrown when JSON response body cannot be decoded
+* Added configurable magic method calls to clients and commands. This is off by default.
+* Fixed a defect that added a hash to every parsed URL part
+* Fixed duplicate none generation for OauthPlugin.
+* Emitting an event each time a client is generated by a ServiceBuilder
+* Using an ApiParams object instead of a Collection for parameters of an ApiCommand
+* cache.* request parameters should be renamed to params.cache.*
+* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc.). See CurlHandle.
+* Added the ability to disable type validation of service descriptions
+* ServiceDescriptions and ServiceBuilders are now Serializable
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/LICENSE b/bin/wiki/vendor/guzzlehttp/guzzle/LICENSE
new file mode 100644
index 00000000..50a177b0
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011-2018 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/README.md b/bin/wiki/vendor/guzzlehttp/guzzle/README.md
new file mode 100644
index 00000000..bcd18b8e
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/README.md
@@ -0,0 +1,91 @@
+Guzzle, PHP HTTP client
+=======================
+
+[![Latest Version](https://img.shields.io/github/release/guzzle/guzzle.svg?style=flat-square)](https://github.com/guzzle/guzzle/releases)
+[![Build Status](https://img.shields.io/travis/guzzle/guzzle.svg?style=flat-square)](https://travis-ci.org/guzzle/guzzle)
+[![Total Downloads](https://img.shields.io/packagist/dt/guzzlehttp/guzzle.svg?style=flat-square)](https://packagist.org/packages/guzzlehttp/guzzle)
+
+Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
+trivial to integrate with web services.
+
+- Simple interface for building query strings, POST requests, streaming large
+ uploads, streaming large downloads, using HTTP cookies, uploading JSON data,
+ etc...
+- Can send both synchronous and asynchronous requests using the same interface.
+- Uses PSR-7 interfaces for requests, responses, and streams. This allows you
+ to utilize other PSR-7 compatible libraries with Guzzle.
+- Abstracts away the underlying HTTP transport, allowing you to write
+ environment and transport agnostic code; i.e., no hard dependency on cURL,
+ PHP streams, sockets, or non-blocking event loops.
+- Middleware system allows you to augment and compose client behavior.
+
+```php
+$client = new \GuzzleHttp\Client();
+$res = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle');
+echo $res->getStatusCode();
+// 200
+echo $res->getHeaderLine('content-type');
+// 'application/json; charset=utf8'
+echo $res->getBody();
+// '{"id": 1420053, "name": "guzzle", ...}'
+
+// Send an asynchronous request.
+$request = new \GuzzleHttp\Psr7\Request('GET', 'http://httpbin.org');
+$promise = $client->sendAsync($request)->then(function ($response) {
+ echo 'I completed! ' . $response->getBody();
+});
+$promise->wait();
+```
+
+## Help and docs
+
+- [Documentation](http://guzzlephp.org/)
+- [Stack Overflow](http://stackoverflow.com/questions/tagged/guzzle)
+- [Gitter](https://gitter.im/guzzle/guzzle)
+
+
+## Installing Guzzle
+
+The recommended way to install Guzzle is through
+[Composer](http://getcomposer.org).
+
+```bash
+# Install Composer
+curl -sS https://getcomposer.org/installer | php
+```
+
+Next, run the Composer command to install the latest stable version of Guzzle:
+
+```bash
+php composer.phar require guzzlehttp/guzzle
+```
+
+After installing, you need to require Composer's autoloader:
+
+```php
+require 'vendor/autoload.php';
+```
+
+You can then later update Guzzle using composer:
+
+ ```bash
+composer.phar update
+ ```
+
+
+## Version Guidance
+
+| Version | Status | Packagist | Namespace | Repo | Docs | PSR-7 | PHP Version |
+|---------|------------|---------------------|--------------|---------------------|---------------------|-------|-------------|
+| 3.x | EOL | `guzzle/guzzle` | `Guzzle` | [v3][guzzle-3-repo] | [v3][guzzle-3-docs] | No | >= 5.3.3 |
+| 4.x | EOL | `guzzlehttp/guzzle` | `GuzzleHttp` | [v4][guzzle-4-repo] | N/A | No | >= 5.4 |
+| 5.x | Maintained | `guzzlehttp/guzzle` | `GuzzleHttp` | [v5][guzzle-5-repo] | [v5][guzzle-5-docs] | No | >= 5.4 |
+| 6.x | Latest | `guzzlehttp/guzzle` | `GuzzleHttp` | [v6][guzzle-6-repo] | [v6][guzzle-6-docs] | Yes | >= 5.5 |
+
+[guzzle-3-repo]: https://github.com/guzzle/guzzle3
+[guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x
+[guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3
+[guzzle-6-repo]: https://github.com/guzzle/guzzle
+[guzzle-3-docs]: http://guzzle3.readthedocs.org/en/latest/
+[guzzle-5-docs]: http://guzzle.readthedocs.org/en/5.3/
+[guzzle-6-docs]: http://guzzle.readthedocs.org/en/latest/
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/UPGRADING.md b/bin/wiki/vendor/guzzlehttp/guzzle/UPGRADING.md
new file mode 100644
index 00000000..91d1dcc9
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/UPGRADING.md
@@ -0,0 +1,1203 @@
+Guzzle Upgrade Guide
+====================
+
+5.0 to 6.0
+----------
+
+Guzzle now uses [PSR-7](http://www.php-fig.org/psr/psr-7/) for HTTP messages.
+Due to the fact that these messages are immutable, this prompted a refactoring
+of Guzzle to use a middleware based system rather than an event system. Any
+HTTP message interaction (e.g., `GuzzleHttp\Message\Request`) need to be
+updated to work with the new immutable PSR-7 request and response objects. Any
+event listeners or subscribers need to be updated to become middleware
+functions that wrap handlers (or are injected into a
+`GuzzleHttp\HandlerStack`).
+
+- Removed `GuzzleHttp\BatchResults`
+- Removed `GuzzleHttp\Collection`
+- Removed `GuzzleHttp\HasDataTrait`
+- Removed `GuzzleHttp\ToArrayInterface`
+- The `guzzlehttp/streams` dependency has been removed. Stream functionality
+ is now present in the `GuzzleHttp\Psr7` namespace provided by the
+ `guzzlehttp/psr7` package.
+- Guzzle no longer uses ReactPHP promises and now uses the
+ `guzzlehttp/promises` library. We use a custom promise library for three
+ significant reasons:
+ 1. React promises (at the time of writing this) are recursive. Promise
+ chaining and promise resolution will eventually blow the stack. Guzzle
+ promises are not recursive as they use a sort of trampolining technique.
+ Note: there has been movement in the React project to modify promises to
+ no longer utilize recursion.
+ 2. Guzzle needs to have the ability to synchronously block on a promise to
+ wait for a result. Guzzle promises allows this functionality (and does
+ not require the use of recursion).
+ 3. Because we need to be able to wait on a result, doing so using React
+ promises requires wrapping react promises with RingPHP futures. This
+ overhead is no longer needed, reducing stack sizes, reducing complexity,
+ and improving performance.
+- `GuzzleHttp\Mimetypes` has been moved to a function in
+ `GuzzleHttp\Psr7\mimetype_from_extension` and
+ `GuzzleHttp\Psr7\mimetype_from_filename`.
+- `GuzzleHttp\Query` and `GuzzleHttp\QueryParser` have been removed. Query
+ strings must now be passed into request objects as strings, or provided to
+ the `query` request option when creating requests with clients. The `query`
+ option uses PHP's `http_build_query` to convert an array to a string. If you
+ need a different serialization technique, you will need to pass the query
+ string in as a string. There are a couple helper functions that will make
+ working with query strings easier: `GuzzleHttp\Psr7\parse_query` and
+ `GuzzleHttp\Psr7\build_query`.
+- Guzzle no longer has a dependency on RingPHP. Due to the use of a middleware
+ system based on PSR-7, using RingPHP and it's middleware system as well adds
+ more complexity than the benefits it provides. All HTTP handlers that were
+ present in RingPHP have been modified to work directly with PSR-7 messages
+ and placed in the `GuzzleHttp\Handler` namespace. This significantly reduces
+ complexity in Guzzle, removes a dependency, and improves performance. RingPHP
+ will be maintained for Guzzle 5 support, but will no longer be a part of
+ Guzzle 6.
+- As Guzzle now uses a middleware based systems the event system and RingPHP
+ integration has been removed. Note: while the event system has been removed,
+ it is possible to add your own type of event system that is powered by the
+ middleware system.
+ - Removed the `Event` namespace.
+ - Removed the `Subscriber` namespace.
+ - Removed `Transaction` class
+ - Removed `RequestFsm`
+ - Removed `RingBridge`
+ - `GuzzleHttp\Subscriber\Cookie` is now provided by
+ `GuzzleHttp\Middleware::cookies`
+ - `GuzzleHttp\Subscriber\HttpError` is now provided by
+ `GuzzleHttp\Middleware::httpError`
+ - `GuzzleHttp\Subscriber\History` is now provided by
+ `GuzzleHttp\Middleware::history`
+ - `GuzzleHttp\Subscriber\Mock` is now provided by
+ `GuzzleHttp\Handler\MockHandler`
+ - `GuzzleHttp\Subscriber\Prepare` is now provided by
+ `GuzzleHttp\PrepareBodyMiddleware`
+ - `GuzzleHttp\Subscriber\Redirect` is now provided by
+ `GuzzleHttp\RedirectMiddleware`
+- Guzzle now uses `Psr\Http\Message\UriInterface` (implements in
+ `GuzzleHttp\Psr7\Uri`) for URI support. `GuzzleHttp\Url` is now gone.
+- Static functions in `GuzzleHttp\Utils` have been moved to namespaced
+ functions under the `GuzzleHttp` namespace. This requires either a Composer
+ based autoloader or you to include functions.php.
+- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
+ `GuzzleHttp\ClientInterface::getConfig`.
+- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
+- The `json` and `xml` methods of response objects has been removed. With the
+ migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
+ adding methods to message interfaces would actually require Guzzle messages
+ to extend from PSR-7 messages rather then work with them directly.
+
+## Migrating to middleware
+
+The change to PSR-7 unfortunately required significant refactoring to Guzzle
+due to the fact that PSR-7 messages are immutable. Guzzle 5 relied on an event
+system from plugins. The event system relied on mutability of HTTP messages and
+side effects in order to work. With immutable messages, you have to change your
+workflow to become more about either returning a value (e.g., functional
+middlewares) or setting a value on an object. Guzzle v6 has chosen the
+functional middleware approach.
+
+Instead of using the event system to listen for things like the `before` event,
+you now create a stack based middleware function that intercepts a request on
+the way in and the promise of the response on the way out. This is a much
+simpler and more predictable approach than the event system and works nicely
+with PSR-7 middleware. Due to the use of promises, the middleware system is
+also asynchronous.
+
+v5:
+
+```php
+use GuzzleHttp\Event\BeforeEvent;
+$client = new GuzzleHttp\Client();
+// Get the emitter and listen to the before event.
+$client->getEmitter()->on('before', function (BeforeEvent $e) {
+ // Guzzle v5 events relied on mutation
+ $e->getRequest()->setHeader('X-Foo', 'Bar');
+});
+```
+
+v6:
+
+In v6, you can modify the request before it is sent using the `mapRequest`
+middleware. The idiomatic way in v6 to modify the request/response lifecycle is
+to setup a handler middleware stack up front and inject the handler into a
+client.
+
+```php
+use GuzzleHttp\Middleware;
+// Create a handler stack that has all of the default middlewares attached
+$handler = GuzzleHttp\HandlerStack::create();
+// Push the handler onto the handler stack
+$handler->push(Middleware::mapRequest(function (RequestInterface $request) {
+ // Notice that we have to return a request object
+ return $request->withHeader('X-Foo', 'Bar');
+}));
+// Inject the handler into the client
+$client = new GuzzleHttp\Client(['handler' => $handler]);
+```
+
+## POST Requests
+
+This version added the [`form_params`](http://guzzle.readthedocs.org/en/latest/request-options.html#form_params)
+and `multipart` request options. `form_params` is an associative array of
+strings or array of strings and is used to serialize an
+`application/x-www-form-urlencoded` POST request. The
+[`multipart`](http://guzzle.readthedocs.org/en/latest/request-options.html#multipart)
+option is now used to send a multipart/form-data POST request.
+
+`GuzzleHttp\Post\PostFile` has been removed. Use the `multipart` option to add
+POST files to a multipart/form-data request.
+
+The `body` option no longer accepts an array to send POST requests. Please use
+`multipart` or `form_params` instead.
+
+The `base_url` option has been renamed to `base_uri`.
+
+4.x to 5.0
+----------
+
+## Rewritten Adapter Layer
+
+Guzzle now uses [RingPHP](http://ringphp.readthedocs.org/en/latest) to send
+HTTP requests. The `adapter` option in a `GuzzleHttp\Client` constructor
+is still supported, but it has now been renamed to `handler`. Instead of
+passing a `GuzzleHttp\Adapter\AdapterInterface`, you must now pass a PHP
+`callable` that follows the RingPHP specification.
+
+## Removed Fluent Interfaces
+
+[Fluent interfaces were removed](http://ocramius.github.io/blog/fluent-interfaces-are-evil)
+from the following classes:
+
+- `GuzzleHttp\Collection`
+- `GuzzleHttp\Url`
+- `GuzzleHttp\Query`
+- `GuzzleHttp\Post\PostBody`
+- `GuzzleHttp\Cookie\SetCookie`
+
+## Removed functions.php
+
+Removed "functions.php", so that Guzzle is truly PSR-4 compliant. The following
+functions can be used as replacements.
+
+- `GuzzleHttp\json_decode` -> `GuzzleHttp\Utils::jsonDecode`
+- `GuzzleHttp\get_path` -> `GuzzleHttp\Utils::getPath`
+- `GuzzleHttp\Utils::setPath` -> `GuzzleHttp\set_path`
+- `GuzzleHttp\Pool::batch` -> `GuzzleHttp\batch`. This function is, however,
+ deprecated in favor of using `GuzzleHttp\Pool::batch()`.
+
+The "procedural" global client has been removed with no replacement (e.g.,
+`GuzzleHttp\get()`, `GuzzleHttp\post()`, etc.). Use a `GuzzleHttp\Client`
+object as a replacement.
+
+## `throwImmediately` has been removed
+
+The concept of "throwImmediately" has been removed from exceptions and error
+events. This control mechanism was used to stop a transfer of concurrent
+requests from completing. This can now be handled by throwing the exception or
+by cancelling a pool of requests or each outstanding future request
+individually.
+
+## headers event has been removed
+
+Removed the "headers" event. This event was only useful for changing the
+body a response once the headers of the response were known. You can implement
+a similar behavior in a number of ways. One example might be to use a
+FnStream that has access to the transaction being sent. For example, when the
+first byte is written, you could check if the response headers match your
+expectations, and if so, change the actual stream body that is being
+written to.
+
+## Updates to HTTP Messages
+
+Removed the `asArray` parameter from
+`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
+value as an array, then use the newly added `getHeaderAsArray()` method of
+`MessageInterface`. This change makes the Guzzle interfaces compatible with
+the PSR-7 interfaces.
+
+3.x to 4.0
+----------
+
+## Overarching changes:
+
+- Now requires PHP 5.4 or greater.
+- No longer requires cURL to send requests.
+- Guzzle no longer wraps every exception it throws. Only exceptions that are
+ recoverable are now wrapped by Guzzle.
+- Various namespaces have been removed or renamed.
+- No longer requiring the Symfony EventDispatcher. A custom event dispatcher
+ based on the Symfony EventDispatcher is
+ now utilized in `GuzzleHttp\Event\EmitterInterface` (resulting in significant
+ speed and functionality improvements).
+
+Changes per Guzzle 3.x namespace are described below.
+
+## Batch
+
+The `Guzzle\Batch` namespace has been removed. This is best left to
+third-parties to implement on top of Guzzle's core HTTP library.
+
+## Cache
+
+The `Guzzle\Cache` namespace has been removed. (Todo: No suitable replacement
+has been implemented yet, but hoping to utilize a PSR cache interface).
+
+## Common
+
+- Removed all of the wrapped exceptions. It's better to use the standard PHP
+ library for unrecoverable exceptions.
+- `FromConfigInterface` has been removed.
+- `Guzzle\Common\Version` has been removed. The VERSION constant can be found
+ at `GuzzleHttp\ClientInterface::VERSION`.
+
+### Collection
+
+- `getAll` has been removed. Use `toArray` to convert a collection to an array.
+- `inject` has been removed.
+- `keySearch` has been removed.
+- `getPath` no longer supports wildcard expressions. Use something better like
+ JMESPath for this.
+- `setPath` now supports appending to an existing array via the `[]` notation.
+
+### Events
+
+Guzzle no longer requires Symfony's EventDispatcher component. Guzzle now uses
+`GuzzleHttp\Event\Emitter`.
+
+- `Symfony\Component\EventDispatcher\EventDispatcherInterface` is replaced by
+ `GuzzleHttp\Event\EmitterInterface`.
+- `Symfony\Component\EventDispatcher\EventDispatcher` is replaced by
+ `GuzzleHttp\Event\Emitter`.
+- `Symfony\Component\EventDispatcher\Event` is replaced by
+ `GuzzleHttp\Event\Event`, and Guzzle now has an EventInterface in
+ `GuzzleHttp\Event\EventInterface`.
+- `AbstractHasDispatcher` has moved to a trait, `HasEmitterTrait`, and
+ `HasDispatcherInterface` has moved to `HasEmitterInterface`. Retrieving the
+ event emitter of a request, client, etc. now uses the `getEmitter` method
+ rather than the `getDispatcher` method.
+
+#### Emitter
+
+- Use the `once()` method to add a listener that automatically removes itself
+ the first time it is invoked.
+- Use the `listeners()` method to retrieve a list of event listeners rather than
+ the `getListeners()` method.
+- Use `emit()` instead of `dispatch()` to emit an event from an emitter.
+- Use `attach()` instead of `addSubscriber()` and `detach()` instead of
+ `removeSubscriber()`.
+
+```php
+$mock = new Mock();
+// 3.x
+$request->getEventDispatcher()->addSubscriber($mock);
+$request->getEventDispatcher()->removeSubscriber($mock);
+// 4.x
+$request->getEmitter()->attach($mock);
+$request->getEmitter()->detach($mock);
+```
+
+Use the `on()` method to add a listener rather than the `addListener()` method.
+
+```php
+// 3.x
+$request->getEventDispatcher()->addListener('foo', function (Event $event) { /* ... */ } );
+// 4.x
+$request->getEmitter()->on('foo', function (Event $event, $name) { /* ... */ } );
+```
+
+## Http
+
+### General changes
+
+- The cacert.pem certificate has been moved to `src/cacert.pem`.
+- Added the concept of adapters that are used to transfer requests over the
+ wire.
+- Simplified the event system.
+- Sending requests in parallel is still possible, but batching is no longer a
+ concept of the HTTP layer. Instead, you must use the `complete` and `error`
+ events to asynchronously manage parallel request transfers.
+- `Guzzle\Http\Url` has moved to `GuzzleHttp\Url`.
+- `Guzzle\Http\QueryString` has moved to `GuzzleHttp\Query`.
+- QueryAggregators have been rewritten so that they are simply callable
+ functions.
+- `GuzzleHttp\StaticClient` has been removed. Use the functions provided in
+ `functions.php` for an easy to use static client instance.
+- Exceptions in `GuzzleHttp\Exception` have been updated to all extend from
+ `GuzzleHttp\Exception\TransferException`.
+
+### Client
+
+Calling methods like `get()`, `post()`, `head()`, etc. no longer create and
+return a request, but rather creates a request, sends the request, and returns
+the response.
+
+```php
+// 3.0
+$request = $client->get('/');
+$response = $request->send();
+
+// 4.0
+$response = $client->get('/');
+
+// or, to mirror the previous behavior
+$request = $client->createRequest('GET', '/');
+$response = $client->send($request);
+```
+
+`GuzzleHttp\ClientInterface` has changed.
+
+- The `send` method no longer accepts more than one request. Use `sendAll` to
+ send multiple requests in parallel.
+- `setUserAgent()` has been removed. Use a default request option instead. You
+ could, for example, do something like:
+ `$client->setConfig('defaults/headers/User-Agent', 'Foo/Bar ' . $client::getDefaultUserAgent())`.
+- `setSslVerification()` has been removed. Use default request options instead,
+ like `$client->setConfig('defaults/verify', true)`.
+
+`GuzzleHttp\Client` has changed.
+
+- The constructor now accepts only an associative array. You can include a
+ `base_url` string or array to use a URI template as the base URL of a client.
+ You can also specify a `defaults` key that is an associative array of default
+ request options. You can pass an `adapter` to use a custom adapter,
+ `batch_adapter` to use a custom adapter for sending requests in parallel, or
+ a `message_factory` to change the factory used to create HTTP requests and
+ responses.
+- The client no longer emits a `client.create_request` event.
+- Creating requests with a client no longer automatically utilize a URI
+ template. You must pass an array into a creational method (e.g.,
+ `createRequest`, `get`, `put`, etc.) in order to expand a URI template.
+
+### Messages
+
+Messages no longer have references to their counterparts (i.e., a request no
+longer has a reference to it's response, and a response no loger has a
+reference to its request). This association is now managed through a
+`GuzzleHttp\Adapter\TransactionInterface` object. You can get references to
+these transaction objects using request events that are emitted over the
+lifecycle of a request.
+
+#### Requests with a body
+
+- `GuzzleHttp\Message\EntityEnclosingRequest` and
+ `GuzzleHttp\Message\EntityEnclosingRequestInterface` have been removed. The
+ separation between requests that contain a body and requests that do not
+ contain a body has been removed, and now `GuzzleHttp\Message\RequestInterface`
+ handles both use cases.
+- Any method that previously accepts a `GuzzleHttp\Response` object now accept a
+ `GuzzleHttp\Message\ResponseInterface`.
+- `GuzzleHttp\Message\RequestFactoryInterface` has been renamed to
+ `GuzzleHttp\Message\MessageFactoryInterface`. This interface is used to create
+ both requests and responses and is implemented in
+ `GuzzleHttp\Message\MessageFactory`.
+- POST field and file methods have been removed from the request object. You
+ must now use the methods made available to `GuzzleHttp\Post\PostBodyInterface`
+ to control the format of a POST body. Requests that are created using a
+ standard `GuzzleHttp\Message\MessageFactoryInterface` will automatically use
+ a `GuzzleHttp\Post\PostBody` body if the body was passed as an array or if
+ the method is POST and no body is provided.
+
+```php
+$request = $client->createRequest('POST', '/');
+$request->getBody()->setField('foo', 'bar');
+$request->getBody()->addFile(new PostFile('file_key', fopen('/path/to/content', 'r')));
+```
+
+#### Headers
+
+- `GuzzleHttp\Message\Header` has been removed. Header values are now simply
+ represented by an array of values or as a string. Header values are returned
+ as a string by default when retrieving a header value from a message. You can
+ pass an optional argument of `true` to retrieve a header value as an array
+ of strings instead of a single concatenated string.
+- `GuzzleHttp\PostFile` and `GuzzleHttp\PostFileInterface` have been moved to
+ `GuzzleHttp\Post`. This interface has been simplified and now allows the
+ addition of arbitrary headers.
+- Custom headers like `GuzzleHttp\Message\Header\Link` have been removed. Most
+ of the custom headers are now handled separately in specific
+ subscribers/plugins, and `GuzzleHttp\Message\HeaderValues::parseParams()` has
+ been updated to properly handle headers that contain parameters (like the
+ `Link` header).
+
+#### Responses
+
+- `GuzzleHttp\Message\Response::getInfo()` and
+ `GuzzleHttp\Message\Response::setInfo()` have been removed. Use the event
+ system to retrieve this type of information.
+- `GuzzleHttp\Message\Response::getRawHeaders()` has been removed.
+- `GuzzleHttp\Message\Response::getMessage()` has been removed.
+- `GuzzleHttp\Message\Response::calculateAge()` and other cache specific
+ methods have moved to the CacheSubscriber.
+- Header specific helper functions like `getContentMd5()` have been removed.
+ Just use `getHeader('Content-MD5')` instead.
+- `GuzzleHttp\Message\Response::setRequest()` and
+ `GuzzleHttp\Message\Response::getRequest()` have been removed. Use the event
+ system to work with request and response objects as a transaction.
+- `GuzzleHttp\Message\Response::getRedirectCount()` has been removed. Use the
+ Redirect subscriber instead.
+- `GuzzleHttp\Message\Response::isSuccessful()` and other related methods have
+ been removed. Use `getStatusCode()` instead.
+
+#### Streaming responses
+
+Streaming requests can now be created by a client directly, returning a
+`GuzzleHttp\Message\ResponseInterface` object that contains a body stream
+referencing an open PHP HTTP stream.
+
+```php
+// 3.0
+use Guzzle\Stream\PhpStreamRequestFactory;
+$request = $client->get('/');
+$factory = new PhpStreamRequestFactory();
+$stream = $factory->fromRequest($request);
+$data = $stream->read(1024);
+
+// 4.0
+$response = $client->get('/', ['stream' => true]);
+// Read some data off of the stream in the response body
+$data = $response->getBody()->read(1024);
+```
+
+#### Redirects
+
+The `configureRedirects()` method has been removed in favor of a
+`allow_redirects` request option.
+
+```php
+// Standard redirects with a default of a max of 5 redirects
+$request = $client->createRequest('GET', '/', ['allow_redirects' => true]);
+
+// Strict redirects with a custom number of redirects
+$request = $client->createRequest('GET', '/', [
+ 'allow_redirects' => ['max' => 5, 'strict' => true]
+]);
+```
+
+#### EntityBody
+
+EntityBody interfaces and classes have been removed or moved to
+`GuzzleHttp\Stream`. All classes and interfaces that once required
+`GuzzleHttp\EntityBodyInterface` now require
+`GuzzleHttp\Stream\StreamInterface`. Creating a new body for a request no
+longer uses `GuzzleHttp\EntityBody::factory` but now uses
+`GuzzleHttp\Stream\Stream::factory` or even better:
+`GuzzleHttp\Stream\create()`.
+
+- `Guzzle\Http\EntityBodyInterface` is now `GuzzleHttp\Stream\StreamInterface`
+- `Guzzle\Http\EntityBody` is now `GuzzleHttp\Stream\Stream`
+- `Guzzle\Http\CachingEntityBody` is now `GuzzleHttp\Stream\CachingStream`
+- `Guzzle\Http\ReadLimitEntityBody` is now `GuzzleHttp\Stream\LimitStream`
+- `Guzzle\Http\IoEmittyinEntityBody` has been removed.
+
+#### Request lifecycle events
+
+Requests previously submitted a large number of requests. The number of events
+emitted over the lifecycle of a request has been significantly reduced to make
+it easier to understand how to extend the behavior of a request. All events
+emitted during the lifecycle of a request now emit a custom
+`GuzzleHttp\Event\EventInterface` object that contains context providing
+methods and a way in which to modify the transaction at that specific point in
+time (e.g., intercept the request and set a response on the transaction).
+
+- `request.before_send` has been renamed to `before` and now emits a
+ `GuzzleHttp\Event\BeforeEvent`
+- `request.complete` has been renamed to `complete` and now emits a
+ `GuzzleHttp\Event\CompleteEvent`.
+- `request.sent` has been removed. Use `complete`.
+- `request.success` has been removed. Use `complete`.
+- `error` is now an event that emits a `GuzzleHttp\Event\ErrorEvent`.
+- `request.exception` has been removed. Use `error`.
+- `request.receive.status_line` has been removed.
+- `curl.callback.progress` has been removed. Use a custom `StreamInterface` to
+ maintain a status update.
+- `curl.callback.write` has been removed. Use a custom `StreamInterface` to
+ intercept writes.
+- `curl.callback.read` has been removed. Use a custom `StreamInterface` to
+ intercept reads.
+
+`headers` is a new event that is emitted after the response headers of a
+request have been received before the body of the response is downloaded. This
+event emits a `GuzzleHttp\Event\HeadersEvent`.
+
+You can intercept a request and inject a response using the `intercept()` event
+of a `GuzzleHttp\Event\BeforeEvent`, `GuzzleHttp\Event\CompleteEvent`, and
+`GuzzleHttp\Event\ErrorEvent` event.
+
+See: http://docs.guzzlephp.org/en/latest/events.html
+
+## Inflection
+
+The `Guzzle\Inflection` namespace has been removed. This is not a core concern
+of Guzzle.
+
+## Iterator
+
+The `Guzzle\Iterator` namespace has been removed.
+
+- `Guzzle\Iterator\AppendIterator`, `Guzzle\Iterator\ChunkedIterator`, and
+ `Guzzle\Iterator\MethodProxyIterator` are nice, but not a core requirement of
+ Guzzle itself.
+- `Guzzle\Iterator\FilterIterator` is no longer needed because an equivalent
+ class is shipped with PHP 5.4.
+- `Guzzle\Iterator\MapIterator` is not really needed when using PHP 5.5 because
+ it's easier to just wrap an iterator in a generator that maps values.
+
+For a replacement of these iterators, see https://github.com/nikic/iter
+
+## Log
+
+The LogPlugin has moved to https://github.com/guzzle/log-subscriber. The
+`Guzzle\Log` namespace has been removed. Guzzle now relies on
+`Psr\Log\LoggerInterface` for all logging. The MessageFormatter class has been
+moved to `GuzzleHttp\Subscriber\Log\Formatter`.
+
+## Parser
+
+The `Guzzle\Parser` namespace has been removed. This was previously used to
+make it possible to plug in custom parsers for cookies, messages, URI
+templates, and URLs; however, this level of complexity is not needed in Guzzle
+so it has been removed.
+
+- Cookie: Cookie parsing logic has been moved to
+ `GuzzleHttp\Cookie\SetCookie::fromString`.
+- Message: Message parsing logic for both requests and responses has been moved
+ to `GuzzleHttp\Message\MessageFactory::fromMessage`. Message parsing is only
+ used in debugging or deserializing messages, so it doesn't make sense for
+ Guzzle as a library to add this level of complexity to parsing messages.
+- UriTemplate: URI template parsing has been moved to
+ `GuzzleHttp\UriTemplate`. The Guzzle library will automatically use the PECL
+ URI template library if it is installed.
+- Url: URL parsing is now performed in `GuzzleHttp\Url::fromString` (previously
+ it was `Guzzle\Http\Url::factory()`). If custom URL parsing is necessary,
+ then developers are free to subclass `GuzzleHttp\Url`.
+
+## Plugin
+
+The `Guzzle\Plugin` namespace has been renamed to `GuzzleHttp\Subscriber`.
+Several plugins are shipping with the core Guzzle library under this namespace.
+
+- `GuzzleHttp\Subscriber\Cookie`: Replaces the old CookiePlugin. Cookie jar
+ code has moved to `GuzzleHttp\Cookie`.
+- `GuzzleHttp\Subscriber\History`: Replaces the old HistoryPlugin.
+- `GuzzleHttp\Subscriber\HttpError`: Throws errors when a bad HTTP response is
+ received.
+- `GuzzleHttp\Subscriber\Mock`: Replaces the old MockPlugin.
+- `GuzzleHttp\Subscriber\Prepare`: Prepares the body of a request just before
+ sending. This subscriber is attached to all requests by default.
+- `GuzzleHttp\Subscriber\Redirect`: Replaces the RedirectPlugin.
+
+The following plugins have been removed (third-parties are free to re-implement
+these if needed):
+
+- `GuzzleHttp\Plugin\Async` has been removed.
+- `GuzzleHttp\Plugin\CurlAuth` has been removed.
+- `GuzzleHttp\Plugin\ErrorResponse\ErrorResponsePlugin` has been removed. This
+ functionality should instead be implemented with event listeners that occur
+ after normal response parsing occurs in the guzzle/command package.
+
+The following plugins are not part of the core Guzzle package, but are provided
+in separate repositories:
+
+- `Guzzle\Http\Plugin\BackoffPlugin` has been rewritten to be much simpler
+ to build custom retry policies using simple functions rather than various
+ chained classes. See: https://github.com/guzzle/retry-subscriber
+- `Guzzle\Http\Plugin\Cache\CachePlugin` has moved to
+ https://github.com/guzzle/cache-subscriber
+- `Guzzle\Http\Plugin\Log\LogPlugin` has moved to
+ https://github.com/guzzle/log-subscriber
+- `Guzzle\Http\Plugin\Md5\Md5Plugin` has moved to
+ https://github.com/guzzle/message-integrity-subscriber
+- `Guzzle\Http\Plugin\Mock\MockPlugin` has moved to
+ `GuzzleHttp\Subscriber\MockSubscriber`.
+- `Guzzle\Http\Plugin\Oauth\OauthPlugin` has moved to
+ https://github.com/guzzle/oauth-subscriber
+
+## Service
+
+The service description layer of Guzzle has moved into two separate packages:
+
+- http://github.com/guzzle/command Provides a high level abstraction over web
+ services by representing web service operations using commands.
+- http://github.com/guzzle/guzzle-services Provides an implementation of
+ guzzle/command that provides request serialization and response parsing using
+ Guzzle service descriptions.
+
+## Stream
+
+Stream have moved to a separate package available at
+https://github.com/guzzle/streams.
+
+`Guzzle\Stream\StreamInterface` has been given a large update to cleanly take
+on the responsibilities of `Guzzle\Http\EntityBody` and
+`Guzzle\Http\EntityBodyInterface` now that they have been removed. The number
+of methods implemented by the `StreamInterface` has been drastically reduced to
+allow developers to more easily extend and decorate stream behavior.
+
+## Removed methods from StreamInterface
+
+- `getStream` and `setStream` have been removed to better encapsulate streams.
+- `getMetadata` and `setMetadata` have been removed in favor of
+ `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `getWrapper`, `getWrapperData`, `getStreamType`, and `getUri` have all been
+ removed. This data is accessible when
+ using streams that implement `GuzzleHttp\Stream\MetadataStreamInterface`.
+- `rewind` has been removed. Use `seek(0)` for a similar behavior.
+
+## Renamed methods
+
+- `detachStream` has been renamed to `detach`.
+- `feof` has been renamed to `eof`.
+- `ftell` has been renamed to `tell`.
+- `readLine` has moved from an instance method to a static class method of
+ `GuzzleHttp\Stream\Stream`.
+
+## Metadata streams
+
+`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
+that contain additional metadata accessible via `getMetadata()`.
+`GuzzleHttp\Stream\StreamInterface::getMetadata` and
+`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
+
+## StreamRequestFactory
+
+The entire concept of the StreamRequestFactory has been removed. The way this
+was used in Guzzle 3 broke the actual interface of sending streaming requests
+(instead of getting back a Response, you got a StreamInterface). Streaming
+PHP requests are now implemented through the `GuzzleHttp\Adapter\StreamAdapter`.
+
+3.6 to 3.7
+----------
+
+### Deprecations
+
+- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.:
+
+```php
+\Guzzle\Common\Version::$emitWarnings = true;
+```
+
+The following APIs and options have been marked as deprecated:
+
+- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead.
+- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead.
+- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated
+- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client.
+- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8.
+- Marked `Guzzle\Common\Collection::inject()` as deprecated.
+- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use
+ `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or
+ `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));`
+
+3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational
+request methods. When paired with a client's configuration settings, these options allow you to specify default settings
+for various aspects of a request. Because these options make other previous configuration options redundant, several
+configuration options and methods of a client and AbstractCommand have been deprecated.
+
+- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`.
+- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`.
+- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')`
+- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0
+
+ $command = $client->getCommand('foo', array(
+ 'command.headers' => array('Test' => '123'),
+ 'command.response_body' => '/path/to/file'
+ ));
+
+ // Should be changed to:
+
+ $command = $client->getCommand('foo', array(
+ 'command.request_options' => array(
+ 'headers' => array('Test' => '123'),
+ 'save_as' => '/path/to/file'
+ )
+ ));
+
+### Interface changes
+
+Additions and changes (you will need to update any implementations or subclasses you may have created):
+
+- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`:
+ createRequest, head, delete, put, patch, post, options, prepareRequest
+- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()`
+- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface`
+- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to
+ `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a
+ resource, string, or EntityBody into the $options parameter to specify the download location of the response.
+- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a
+ default `array()`
+- Added `Guzzle\Stream\StreamInterface::isRepeatable`
+- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods.
+
+The following methods were removed from interfaces. All of these methods are still available in the concrete classes
+that implement them, but you should update your code to use alternative methods:
+
+- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use
+ `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or
+ `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or
+ `$client->setDefaultOption('headers/{header_name}', 'value')`. or
+ `$client->setDefaultOption('headers', array('header_name' => 'value'))`.
+- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`.
+- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail.
+- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail.
+- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin.
+- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin.
+
+### Cache plugin breaking changes
+
+- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a
+ CacheStorageInterface. These two objects and interface will be removed in a future version.
+- Always setting X-cache headers on cached responses
+- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin
+- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface
+ $request, Response $response);`
+- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);`
+- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);`
+- Added `CacheStorageInterface::purge($url)`
+- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin
+ $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache,
+ CanCacheStrategyInterface $canCache = null)`
+- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)`
+
+3.5 to 3.6
+----------
+
+* Mixed casing of headers are now forced to be a single consistent casing across all values for that header.
+* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution
+* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader().
+ For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader().
+ Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request.
+* Specific header implementations can be created for complex headers. When a message creates a header, it uses a
+ HeaderFactory which can map specific headers to specific header classes. There is now a Link header and
+ CacheControl header implementation.
+* Moved getLinks() from Response to just be used on a Link header object.
+
+If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the
+HeaderInterface (e.g. toArray(), getAll(), etc.).
+
+### Interface changes
+
+* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate
+* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti()
+* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in
+ Guzzle\Http\Curl\RequestMediator
+* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string.
+* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface
+* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders()
+
+### Removed deprecated functions
+
+* Removed Guzzle\Parser\ParserRegister::get(). Use getParser()
+* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser().
+
+### Deprecations
+
+* The ability to case-insensitively search for header values
+* Guzzle\Http\Message\Header::hasExactHeader
+* Guzzle\Http\Message\Header::raw. Use getAll()
+* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object
+ instead.
+
+### Other changes
+
+* All response header helper functions return a string rather than mixing Header objects and strings inconsistently
+* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc. are managed by Guzzle
+ directly via interfaces
+* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
+ but are a no-op until removed.
+* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
+ `Guzzle\Service\Command\ArrayCommandInterface`.
+* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
+ on a request while the request is still being transferred
+* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess
+
+3.3 to 3.4
+----------
+
+Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs.
+
+3.2 to 3.3
+----------
+
+### Response::getEtag() quote stripping removed
+
+`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header
+
+### Removed `Guzzle\Http\Utils`
+
+The `Guzzle\Http\Utils` class was removed. This class was only used for testing.
+
+### Stream wrapper and type
+
+`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getStreamType()` are no longer converted to lowercase.
+
+### curl.emit_io became emit_io
+
+Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the
+'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io'
+
+3.1 to 3.2
+----------
+
+### CurlMulti is no longer reused globally
+
+Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added
+to a single client can pollute requests dispatched from other clients.
+
+If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the
+ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is
+created.
+
+```php
+$multi = new Guzzle\Http\Curl\CurlMulti();
+$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json');
+$builder->addListener('service_builder.create_client', function ($event) use ($multi) {
+ $event['client']->setCurlMulti($multi);
+}
+});
+```
+
+### No default path
+
+URLs no longer have a default path value of '/' if no path was specified.
+
+Before:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com/
+```
+
+After:
+
+```php
+$request = $client->get('http://www.foo.com');
+echo $request->getUrl();
+// >> http://www.foo.com
+```
+
+### Less verbose BadResponseException
+
+The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and
+response information. You can, however, get access to the request and response object by calling `getRequest()` or
+`getResponse()` on the exception object.
+
+### Query parameter aggregation
+
+Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a
+setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is
+responsible for handling the aggregation of multi-valued query string variables into a flattened hash.
+
+2.8 to 3.x
+----------
+
+### Guzzle\Service\Inspector
+
+Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig`
+
+**Before**
+
+```php
+use Guzzle\Service\Inspector;
+
+class YourClient extends \Guzzle\Service\Client
+{
+ public static function factory($config = array())
+ {
+ $default = array();
+ $required = array('base_url', 'username', 'api_key');
+ $config = Inspector::fromConfig($config, $default, $required);
+
+ $client = new self(
+ $config->get('base_url'),
+ $config->get('username'),
+ $config->get('api_key')
+ );
+ $client->setConfig($config);
+
+ $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+ return $client;
+ }
+```
+
+**After**
+
+```php
+use Guzzle\Common\Collection;
+
+class YourClient extends \Guzzle\Service\Client
+{
+ public static function factory($config = array())
+ {
+ $default = array();
+ $required = array('base_url', 'username', 'api_key');
+ $config = Collection::fromConfig($config, $default, $required);
+
+ $client = new self(
+ $config->get('base_url'),
+ $config->get('username'),
+ $config->get('api_key')
+ );
+ $client->setConfig($config);
+
+ $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json'));
+
+ return $client;
+ }
+```
+
+### Convert XML Service Descriptions to JSON
+
+**Before**
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<client>
+ <commands>
+ <!-- Groups -->
+ <command name="list_groups" method="GET" uri="groups.json">
+ <doc>Get a list of groups</doc>
+ </command>
+ <command name="search_groups" method="GET" uri='search.json?query="{{query}} type:group"'>
+ <doc>Uses a search query to get a list of groups</doc>
+ <param name="query" type="string" required="true" />
+ </command>
+ <command name="create_group" method="POST" uri="groups.json">
+ <doc>Create a group</doc>
+ <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
+ <param name="Content-Type" location="header" static="application/json"/>
+ </command>
+ <command name="delete_group" method="DELETE" uri="groups/{{id}}.json">
+ <doc>Delete a group by ID</doc>
+ <param name="id" type="integer" required="true"/>
+ </command>
+ <command name="get_group" method="GET" uri="groups/{{id}}.json">
+ <param name="id" type="integer" required="true"/>
+ </command>
+ <command name="update_group" method="PUT" uri="groups/{{id}}.json">
+ <doc>Update a group</doc>
+ <param name="id" type="integer" required="true"/>
+ <param name="data" type="array" location="body" filters="json_encode" doc="Group JSON"/>
+ <param name="Content-Type" location="header" static="application/json"/>
+ </command>
+ </commands>
+</client>
+```
+
+**After**
+
+```json
+{
+ "name": "Zendesk REST API v2",
+ "apiVersion": "2012-12-31",
+ "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users",
+ "operations": {
+ "list_groups": {
+ "httpMethod":"GET",
+ "uri": "groups.json",
+ "summary": "Get a list of groups"
+ },
+ "search_groups":{
+ "httpMethod":"GET",
+ "uri": "search.json?query=\"{query} type:group\"",
+ "summary": "Uses a search query to get a list of groups",
+ "parameters":{
+ "query":{
+ "location": "uri",
+ "description":"Zendesk Search Query",
+ "type": "string",
+ "required": true
+ }
+ }
+ },
+ "create_group": {
+ "httpMethod":"POST",
+ "uri": "groups.json",
+ "summary": "Create a group",
+ "parameters":{
+ "data": {
+ "type": "array",
+ "location": "body",
+ "description":"Group JSON",
+ "filters": "json_encode",
+ "required": true
+ },
+ "Content-Type":{
+ "type": "string",
+ "location":"header",
+ "static": "application/json"
+ }
+ }
+ },
+ "delete_group": {
+ "httpMethod":"DELETE",
+ "uri": "groups/{id}.json",
+ "summary": "Delete a group",
+ "parameters":{
+ "id":{
+ "location": "uri",
+ "description":"Group to delete by ID",
+ "type": "integer",
+ "required": true
+ }
+ }
+ },
+ "get_group": {
+ "httpMethod":"GET",
+ "uri": "groups/{id}.json",
+ "summary": "Get a ticket",
+ "parameters":{
+ "id":{
+ "location": "uri",
+ "description":"Group to get by ID",
+ "type": "integer",
+ "required": true
+ }
+ }
+ },
+ "update_group": {
+ "httpMethod":"PUT",
+ "uri": "groups/{id}.json",
+ "summary": "Update a group",
+ "parameters":{
+ "id": {
+ "location": "uri",
+ "description":"Group to update by ID",
+ "type": "integer",
+ "required": true
+ },
+ "data": {
+ "type": "array",
+ "location": "body",
+ "description":"Group JSON",
+ "filters": "json_encode",
+ "required": true
+ },
+ "Content-Type":{
+ "type": "string",
+ "location":"header",
+ "static": "application/json"
+ }
+ }
+ }
+}
+```
+
+### Guzzle\Service\Description\ServiceDescription
+
+Commands are now called Operations
+
+**Before**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getCommands(); // @returns ApiCommandInterface[]
+$sd->hasCommand($name);
+$sd->getCommand($name); // @returns ApiCommandInterface|null
+$sd->addCommand($command); // @param ApiCommandInterface $command
+```
+
+**After**
+
+```php
+use Guzzle\Service\Description\ServiceDescription;
+
+$sd = new ServiceDescription();
+$sd->getOperations(); // @returns OperationInterface[]
+$sd->hasOperation($name);
+$sd->getOperation($name); // @returns OperationInterface|null
+$sd->addOperation($operation); // @param OperationInterface $operation
+```
+
+### Guzzle\Common\Inflection\Inflector
+
+Namespace is now `Guzzle\Inflection\Inflector`
+
+### Guzzle\Http\Plugin
+
+Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below.
+
+### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log
+
+Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively.
+
+**Before**
+
+```php
+use Guzzle\Common\Log\ClosureLogAdapter;
+use Guzzle\Http\Plugin\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $verbosity is an integer indicating desired message verbosity level
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE);
+```
+
+**After**
+
+```php
+use Guzzle\Log\ClosureLogAdapter;
+use Guzzle\Log\MessageFormatter;
+use Guzzle\Plugin\Log\LogPlugin;
+
+/** @var \Guzzle\Http\Client */
+$client;
+
+// $format is a string indicating desired message format -- @see MessageFormatter
+$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT);
+```
+
+### Guzzle\Http\Plugin\CurlAuthPlugin
+
+Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`.
+
+### Guzzle\Http\Plugin\ExponentialBackoffPlugin
+
+Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes.
+
+**Before**
+
+```php
+use Guzzle\Http\Plugin\ExponentialBackoffPlugin;
+
+$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge(
+ ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429)
+ ));
+
+$client->addSubscriber($backoffPlugin);
+```
+
+**After**
+
+```php
+use Guzzle\Plugin\Backoff\BackoffPlugin;
+use Guzzle\Plugin\Backoff\HttpBackoffStrategy;
+
+// Use convenient factory method instead -- see implementation for ideas of what
+// you can do with chaining backoff strategies
+$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge(
+ HttpBackoffStrategy::getDefaultFailureCodes(), array(429)
+ ));
+$client->addSubscriber($backoffPlugin);
+```
+
+### Known Issues
+
+#### [BUG] Accept-Encoding header behavior changed unintentionally.
+
+(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e)
+
+In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to
+properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen.
+See issue #217 for a workaround, or use a version containing the fix.
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/composer.json b/bin/wiki/vendor/guzzlehttp/guzzle/composer.json
new file mode 100644
index 00000000..1f328e30
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/composer.json
@@ -0,0 +1,44 @@
+{
+ "name": "guzzlehttp/guzzle",
+ "type": "library",
+ "description": "Guzzle is a PHP HTTP client library",
+ "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
+ "homepage": "http://guzzlephp.org/",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.5",
+ "guzzlehttp/psr7": "^1.4",
+ "guzzlehttp/promises": "^1.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+ "psr/log": "^1.0"
+ },
+ "autoload": {
+ "files": ["src/functions_include.php"],
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "GuzzleHttp\\Tests\\": "tests/"
+ }
+ },
+ "suggest": {
+ "psr/log": "Required for using the Log middleware"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.3-dev"
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Client.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Client.php
new file mode 100644
index 00000000..80417918
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Client.php
@@ -0,0 +1,422 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Cookie\CookieJar;
+use GuzzleHttp\Promise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\UriInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * @method ResponseInterface get(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface head(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface put(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface post(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface patch(string|UriInterface $uri, array $options = [])
+ * @method ResponseInterface delete(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface getAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface headAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface putAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface postAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface patchAsync(string|UriInterface $uri, array $options = [])
+ * @method Promise\PromiseInterface deleteAsync(string|UriInterface $uri, array $options = [])
+ */
+class Client implements ClientInterface
+{
+ /** @var array Default request options */
+ private $config;
+
+ /**
+ * Clients accept an array of constructor parameters.
+ *
+ * Here's an example of creating a client using a base_uri and an array of
+ * default request options to apply to each request:
+ *
+ * $client = new Client([
+ * 'base_uri' => 'http://www.foo.com/1.0/',
+ * 'timeout' => 0,
+ * 'allow_redirects' => false,
+ * 'proxy' => '192.168.16.1:10'
+ * ]);
+ *
+ * Client configuration settings include the following options:
+ *
+ * - handler: (callable) Function that transfers HTTP requests over the
+ * wire. The function is called with a Psr7\Http\Message\RequestInterface
+ * and array of transfer options, and must return a
+ * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a
+ * Psr7\Http\Message\ResponseInterface on success. "handler" is a
+ * constructor only option that cannot be overridden in per/request
+ * options. If no handler is provided, a default handler will be created
+ * that enables all of the request options below by attaching all of the
+ * default middleware to the handler.
+ * - base_uri: (string|UriInterface) Base URI of the client that is merged
+ * into relative URIs. Can be a string or instance of UriInterface.
+ * - **: any request option
+ *
+ * @param array $config Client configuration settings.
+ *
+ * @see \GuzzleHttp\RequestOptions for a list of available request options.
+ */
+ public function __construct(array $config = [])
+ {
+ if (!isset($config['handler'])) {
+ $config['handler'] = HandlerStack::create();
+ } elseif (!is_callable($config['handler'])) {
+ throw new \InvalidArgumentException('handler must be a callable');
+ }
+
+ // Convert the base_uri to a UriInterface
+ if (isset($config['base_uri'])) {
+ $config['base_uri'] = Psr7\uri_for($config['base_uri']);
+ }
+
+ $this->configureDefaults($config);
+ }
+
+ public function __call($method, $args)
+ {
+ if (count($args) < 1) {
+ throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
+ }
+
+ $uri = $args[0];
+ $opts = isset($args[1]) ? $args[1] : [];
+
+ return substr($method, -5) === 'Async'
+ ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
+ : $this->request($method, $uri, $opts);
+ }
+
+ public function sendAsync(RequestInterface $request, array $options = [])
+ {
+ // Merge the base URI into the request URI if needed.
+ $options = $this->prepareDefaults($options);
+
+ return $this->transfer(
+ $request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')),
+ $options
+ );
+ }
+
+ public function send(RequestInterface $request, array $options = [])
+ {
+ $options[RequestOptions::SYNCHRONOUS] = true;
+ return $this->sendAsync($request, $options)->wait();
+ }
+
+ public function requestAsync($method, $uri = '', array $options = [])
+ {
+ $options = $this->prepareDefaults($options);
+ // Remove request modifying parameter because it can be done up-front.
+ $headers = isset($options['headers']) ? $options['headers'] : [];
+ $body = isset($options['body']) ? $options['body'] : null;
+ $version = isset($options['version']) ? $options['version'] : '1.1';
+ // Merge the URI into the base URI.
+ $uri = $this->buildUri($uri, $options);
+ if (is_array($body)) {
+ $this->invalidBody();
+ }
+ $request = new Psr7\Request($method, $uri, $headers, $body, $version);
+ // Remove the option so that they are not doubly-applied.
+ unset($options['headers'], $options['body'], $options['version']);
+
+ return $this->transfer($request, $options);
+ }
+
+ public function request($method, $uri = '', array $options = [])
+ {
+ $options[RequestOptions::SYNCHRONOUS] = true;
+ return $this->requestAsync($method, $uri, $options)->wait();
+ }
+
+ public function getConfig($option = null)
+ {
+ return $option === null
+ ? $this->config
+ : (isset($this->config[$option]) ? $this->config[$option] : null);
+ }
+
+ private function buildUri($uri, array $config)
+ {
+ // for BC we accept null which would otherwise fail in uri_for
+ $uri = Psr7\uri_for($uri === null ? '' : $uri);
+
+ if (isset($config['base_uri'])) {
+ $uri = Psr7\UriResolver::resolve(Psr7\uri_for($config['base_uri']), $uri);
+ }
+
+ return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri;
+ }
+
+ /**
+ * Configures the default options for a client.
+ *
+ * @param array $config
+ */
+ private function configureDefaults(array $config)
+ {
+ $defaults = [
+ 'allow_redirects' => RedirectMiddleware::$defaultSettings,
+ 'http_errors' => true,
+ 'decode_content' => true,
+ 'verify' => true,
+ 'cookies' => false
+ ];
+
+ // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
+
+ // We can only trust the HTTP_PROXY environment variable in a CLI
+ // process due to the fact that PHP has no reliable mechanism to
+ // get environment variables that start with "HTTP_".
+ if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
+ $defaults['proxy']['http'] = getenv('HTTP_PROXY');
+ }
+
+ if ($proxy = getenv('HTTPS_PROXY')) {
+ $defaults['proxy']['https'] = $proxy;
+ }
+
+ if ($noProxy = getenv('NO_PROXY')) {
+ $cleanedNoProxy = str_replace(' ', '', $noProxy);
+ $defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
+ }
+
+ $this->config = $config + $defaults;
+
+ if (!empty($config['cookies']) && $config['cookies'] === true) {
+ $this->config['cookies'] = new CookieJar();
+ }
+
+ // Add the default user-agent header.
+ if (!isset($this->config['headers'])) {
+ $this->config['headers'] = ['User-Agent' => default_user_agent()];
+ } else {
+ // Add the User-Agent header if one was not already set.
+ foreach (array_keys($this->config['headers']) as $name) {
+ if (strtolower($name) === 'user-agent') {
+ return;
+ }
+ }
+ $this->config['headers']['User-Agent'] = default_user_agent();
+ }
+ }
+
+ /**
+ * Merges default options into the array.
+ *
+ * @param array $options Options to modify by reference
+ *
+ * @return array
+ */
+ private function prepareDefaults($options)
+ {
+ $defaults = $this->config;
+
+ if (!empty($defaults['headers'])) {
+ // Default headers are only added if they are not present.
+ $defaults['_conditional'] = $defaults['headers'];
+ unset($defaults['headers']);
+ }
+
+ // Special handling for headers is required as they are added as
+ // conditional headers and as headers passed to a request ctor.
+ if (array_key_exists('headers', $options)) {
+ // Allows default headers to be unset.
+ if ($options['headers'] === null) {
+ $defaults['_conditional'] = null;
+ unset($options['headers']);
+ } elseif (!is_array($options['headers'])) {
+ throw new \InvalidArgumentException('headers must be an array');
+ }
+ }
+
+ // Shallow merge defaults underneath options.
+ $result = $options + $defaults;
+
+ // Remove null values.
+ foreach ($result as $k => $v) {
+ if ($v === null) {
+ unset($result[$k]);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Transfers the given request and applies request options.
+ *
+ * The URI of the request is not modified and the request options are used
+ * as-is without merging in default options.
+ *
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return Promise\PromiseInterface
+ */
+ private function transfer(RequestInterface $request, array $options)
+ {
+ // save_to -> sink
+ if (isset($options['save_to'])) {
+ $options['sink'] = $options['save_to'];
+ unset($options['save_to']);
+ }
+
+ // exceptions -> http_errors
+ if (isset($options['exceptions'])) {
+ $options['http_errors'] = $options['exceptions'];
+ unset($options['exceptions']);
+ }
+
+ $request = $this->applyOptions($request, $options);
+ $handler = $options['handler'];
+
+ try {
+ return Promise\promise_for($handler($request, $options));
+ } catch (\Exception $e) {
+ return Promise\rejection_for($e);
+ }
+ }
+
+ /**
+ * Applies the array of request options to a request.
+ *
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return RequestInterface
+ */
+ private function applyOptions(RequestInterface $request, array &$options)
+ {
+ $modify = [
+ 'set_headers' => [],
+ ];
+
+ if (isset($options['headers'])) {
+ $modify['set_headers'] = $options['headers'];
+ unset($options['headers']);
+ }
+
+ if (isset($options['form_params'])) {
+ if (isset($options['multipart'])) {
+ throw new \InvalidArgumentException('You cannot use '
+ . 'form_params and multipart at the same time. Use the '
+ . 'form_params option if you want to send application/'
+ . 'x-www-form-urlencoded requests, and the multipart '
+ . 'option to send multipart/form-data requests.');
+ }
+ $options['body'] = http_build_query($options['form_params'], '', '&');
+ unset($options['form_params']);
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
+ $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded';
+ }
+
+ if (isset($options['multipart'])) {
+ $options['body'] = new Psr7\MultipartStream($options['multipart']);
+ unset($options['multipart']);
+ }
+
+ if (isset($options['json'])) {
+ $options['body'] = \GuzzleHttp\json_encode($options['json']);
+ unset($options['json']);
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
+ $options['_conditional']['Content-Type'] = 'application/json';
+ }
+
+ if (!empty($options['decode_content'])
+ && $options['decode_content'] !== true
+ ) {
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Accept-Encoding'], $options['_conditional']);
+ $modify['set_headers']['Accept-Encoding'] = $options['decode_content'];
+ }
+
+ if (isset($options['body'])) {
+ if (is_array($options['body'])) {
+ $this->invalidBody();
+ }
+ $modify['body'] = Psr7\stream_for($options['body']);
+ unset($options['body']);
+ }
+
+ if (!empty($options['auth']) && is_array($options['auth'])) {
+ $value = $options['auth'];
+ $type = isset($value[2]) ? strtolower($value[2]) : 'basic';
+ switch ($type) {
+ case 'basic':
+ // Ensure that we don't have the header in different case and set the new value.
+ $modify['set_headers'] = Psr7\_caseless_remove(['Authorization'], $modify['set_headers']);
+ $modify['set_headers']['Authorization'] = 'Basic '
+ . base64_encode("$value[0]:$value[1]");
+ break;
+ case 'digest':
+ // @todo: Do not rely on curl
+ $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_DIGEST;
+ $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
+ break;
+ case 'ntlm':
+ $options['curl'][CURLOPT_HTTPAUTH] = CURLAUTH_NTLM;
+ $options['curl'][CURLOPT_USERPWD] = "$value[0]:$value[1]";
+ break;
+ }
+ }
+
+ if (isset($options['query'])) {
+ $value = $options['query'];
+ if (is_array($value)) {
+ $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
+ }
+ if (!is_string($value)) {
+ throw new \InvalidArgumentException('query must be a string or array');
+ }
+ $modify['query'] = $value;
+ unset($options['query']);
+ }
+
+ // Ensure that sink is not an invalid value.
+ if (isset($options['sink'])) {
+ // TODO: Add more sink validation?
+ if (is_bool($options['sink'])) {
+ throw new \InvalidArgumentException('sink must not be a boolean');
+ }
+ }
+
+ $request = Psr7\modify_request($request, $modify);
+ if ($request->getBody() instanceof Psr7\MultipartStream) {
+ // Use a multipart/form-data POST if a Content-Type is not set.
+ // Ensure that we don't have the header in different case and set the new value.
+ $options['_conditional'] = Psr7\_caseless_remove(['Content-Type'], $options['_conditional']);
+ $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
+ . $request->getBody()->getBoundary();
+ }
+
+ // Merge in conditional headers if they are not present.
+ if (isset($options['_conditional'])) {
+ // Build up the changes so it's in a single clone of the message.
+ $modify = [];
+ foreach ($options['_conditional'] as $k => $v) {
+ if (!$request->hasHeader($k)) {
+ $modify['set_headers'][$k] = $v;
+ }
+ }
+ $request = Psr7\modify_request($request, $modify);
+ // Don't pass this internal value along to middleware/handlers.
+ unset($options['_conditional']);
+ }
+
+ return $request;
+ }
+
+ private function invalidBody()
+ {
+ throw new \InvalidArgumentException('Passing in the "body" request '
+ . 'option as an array to send a POST request has been deprecated. '
+ . 'Please use the "form_params" request option to send a '
+ . 'application/x-www-form-urlencoded request, or the "multipart" '
+ . 'request option to send a multipart/form-data request.');
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/ClientInterface.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/ClientInterface.php
new file mode 100644
index 00000000..2dbcffa4
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/ClientInterface.php
@@ -0,0 +1,84 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Exception\GuzzleException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Client interface for sending HTTP requests.
+ */
+interface ClientInterface
+{
+ const VERSION = '6.3.3';
+
+ /**
+ * Send an HTTP request.
+ *
+ * @param RequestInterface $request Request to send
+ * @param array $options Request options to apply to the given
+ * request and to the transfer.
+ *
+ * @return ResponseInterface
+ * @throws GuzzleException
+ */
+ public function send(RequestInterface $request, array $options = []);
+
+ /**
+ * Asynchronously send an HTTP request.
+ *
+ * @param RequestInterface $request Request to send
+ * @param array $options Request options to apply to the given
+ * request and to the transfer.
+ *
+ * @return PromiseInterface
+ */
+ public function sendAsync(RequestInterface $request, array $options = []);
+
+ /**
+ * Create and send an HTTP request.
+ *
+ * Use an absolute path to override the base path of the client, or a
+ * relative path to append to the base path of the client. The URL can
+ * contain the query string as well.
+ *
+ * @param string $method HTTP method.
+ * @param string|UriInterface $uri URI object or string.
+ * @param array $options Request options to apply.
+ *
+ * @return ResponseInterface
+ * @throws GuzzleException
+ */
+ public function request($method, $uri, array $options = []);
+
+ /**
+ * Create and send an asynchronous HTTP request.
+ *
+ * Use an absolute path to override the base path of the client, or a
+ * relative path to append to the base path of the client. The URL can
+ * contain the query string as well. Use an array to provide a URL
+ * template and additional variables to use in the URL template expansion.
+ *
+ * @param string $method HTTP method
+ * @param string|UriInterface $uri URI object or string.
+ * @param array $options Request options to apply.
+ *
+ * @return PromiseInterface
+ */
+ public function requestAsync($method, $uri, array $options = []);
+
+ /**
+ * Get a client configuration option.
+ *
+ * These options include default request options of the client, a "handler"
+ * (if utilized by the concrete client), and a "base_uri" if utilized by
+ * the concrete client.
+ *
+ * @param string|null $option The config option to retrieve.
+ *
+ * @return mixed
+ */
+ public function getConfig($option = null);
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
new file mode 100644
index 00000000..78f2b79f
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php
@@ -0,0 +1,314 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Cookie jar that stores cookies as an array
+ */
+class CookieJar implements CookieJarInterface
+{
+ /** @var SetCookie[] Loaded cookie data */
+ private $cookies = [];
+
+ /** @var bool */
+ private $strictMode;
+
+ /**
+ * @param bool $strictMode Set to true to throw exceptions when invalid
+ * cookies are added to the cookie jar.
+ * @param array $cookieArray Array of SetCookie objects or a hash of
+ * arrays that can be used with the SetCookie
+ * constructor
+ */
+ public function __construct($strictMode = false, $cookieArray = [])
+ {
+ $this->strictMode = $strictMode;
+
+ foreach ($cookieArray as $cookie) {
+ if (!($cookie instanceof SetCookie)) {
+ $cookie = new SetCookie($cookie);
+ }
+ $this->setCookie($cookie);
+ }
+ }
+
+ /**
+ * Create a new Cookie jar from an associative array and domain.
+ *
+ * @param array $cookies Cookies to create the jar from
+ * @param string $domain Domain to set the cookies to
+ *
+ * @return self
+ */
+ public static function fromArray(array $cookies, $domain)
+ {
+ $cookieJar = new self();
+ foreach ($cookies as $name => $value) {
+ $cookieJar->setCookie(new SetCookie([
+ 'Domain' => $domain,
+ 'Name' => $name,
+ 'Value' => $value,
+ 'Discard' => true
+ ]));
+ }
+
+ return $cookieJar;
+ }
+
+ /**
+ * @deprecated
+ */
+ public static function getCookieValue($value)
+ {
+ return $value;
+ }
+
+ /**
+ * Evaluate if this cookie should be persisted to storage
+ * that survives between requests.
+ *
+ * @param SetCookie $cookie Being evaluated.
+ * @param bool $allowSessionCookies If we should persist session cookies
+ * @return bool
+ */
+ public static function shouldPersist(
+ SetCookie $cookie,
+ $allowSessionCookies = false
+ ) {
+ if ($cookie->getExpires() || $allowSessionCookies) {
+ if (!$cookie->getDiscard()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds and returns the cookie based on the name
+ *
+ * @param string $name cookie name to search for
+ * @return SetCookie|null cookie that was found or null if not found
+ */
+ public function getCookieByName($name)
+ {
+ // don't allow a null name
+ if ($name === null) {
+ return null;
+ }
+ foreach ($this->cookies as $cookie) {
+ if ($cookie->getName() !== null && strcasecmp($cookie->getName(), $name) === 0) {
+ return $cookie;
+ }
+ }
+ }
+
+ public function toArray()
+ {
+ return array_map(function (SetCookie $cookie) {
+ return $cookie->toArray();
+ }, $this->getIterator()->getArrayCopy());
+ }
+
+ public function clear($domain = null, $path = null, $name = null)
+ {
+ if (!$domain) {
+ $this->cookies = [];
+ return;
+ } elseif (!$path) {
+ $this->cookies = array_filter(
+ $this->cookies,
+ function (SetCookie $cookie) use ($path, $domain) {
+ return !$cookie->matchesDomain($domain);
+ }
+ );
+ } elseif (!$name) {
+ $this->cookies = array_filter(
+ $this->cookies,
+ function (SetCookie $cookie) use ($path, $domain) {
+ return !($cookie->matchesPath($path) &&
+ $cookie->matchesDomain($domain));
+ }
+ );
+ } else {
+ $this->cookies = array_filter(
+ $this->cookies,
+ function (SetCookie $cookie) use ($path, $domain, $name) {
+ return !($cookie->getName() == $name &&
+ $cookie->matchesPath($path) &&
+ $cookie->matchesDomain($domain));
+ }
+ );
+ }
+ }
+
+ public function clearSessionCookies()
+ {
+ $this->cookies = array_filter(
+ $this->cookies,
+ function (SetCookie $cookie) {
+ return !$cookie->getDiscard() && $cookie->getExpires();
+ }
+ );
+ }
+
+ public function setCookie(SetCookie $cookie)
+ {
+ // If the name string is empty (but not 0), ignore the set-cookie
+ // string entirely.
+ $name = $cookie->getName();
+ if (!$name && $name !== '0') {
+ return false;
+ }
+
+ // Only allow cookies with set and valid domain, name, value
+ $result = $cookie->validate();
+ if ($result !== true) {
+ if ($this->strictMode) {
+ throw new \RuntimeException('Invalid cookie: ' . $result);
+ } else {
+ $this->removeCookieIfEmpty($cookie);
+ return false;
+ }
+ }
+
+ // Resolve conflicts with previously set cookies
+ foreach ($this->cookies as $i => $c) {
+
+ // Two cookies are identical, when their path, and domain are
+ // identical.
+ if ($c->getPath() != $cookie->getPath() ||
+ $c->getDomain() != $cookie->getDomain() ||
+ $c->getName() != $cookie->getName()
+ ) {
+ continue;
+ }
+
+ // The previously set cookie is a discard cookie and this one is
+ // not so allow the new cookie to be set
+ if (!$cookie->getDiscard() && $c->getDiscard()) {
+ unset($this->cookies[$i]);
+ continue;
+ }
+
+ // If the new cookie's expiration is further into the future, then
+ // replace the old cookie
+ if ($cookie->getExpires() > $c->getExpires()) {
+ unset($this->cookies[$i]);
+ continue;
+ }
+
+ // If the value has changed, we better change it
+ if ($cookie->getValue() !== $c->getValue()) {
+ unset($this->cookies[$i]);
+ continue;
+ }
+
+ // The cookie exists, so no need to continue
+ return false;
+ }
+
+ $this->cookies[] = $cookie;
+
+ return true;
+ }
+
+ public function count()
+ {
+ return count($this->cookies);
+ }
+
+ public function getIterator()
+ {
+ return new \ArrayIterator(array_values($this->cookies));
+ }
+
+ public function extractCookies(
+ RequestInterface $request,
+ ResponseInterface $response
+ ) {
+ if ($cookieHeader = $response->getHeader('Set-Cookie')) {
+ foreach ($cookieHeader as $cookie) {
+ $sc = SetCookie::fromString($cookie);
+ if (!$sc->getDomain()) {
+ $sc->setDomain($request->getUri()->getHost());
+ }
+ if (0 !== strpos($sc->getPath(), '/')) {
+ $sc->setPath($this->getCookiePathFromRequest($request));
+ }
+ $this->setCookie($sc);
+ }
+ }
+ }
+
+ /**
+ * Computes cookie path following RFC 6265 section 5.1.4
+ *
+ * @link https://tools.ietf.org/html/rfc6265#section-5.1.4
+ *
+ * @param RequestInterface $request
+ * @return string
+ */
+ private function getCookiePathFromRequest(RequestInterface $request)
+ {
+ $uriPath = $request->getUri()->getPath();
+ if ('' === $uriPath) {
+ return '/';
+ }
+ if (0 !== strpos($uriPath, '/')) {
+ return '/';
+ }
+ if ('/' === $uriPath) {
+ return '/';
+ }
+ if (0 === $lastSlashPos = strrpos($uriPath, '/')) {
+ return '/';
+ }
+
+ return substr($uriPath, 0, $lastSlashPos);
+ }
+
+ public function withCookieHeader(RequestInterface $request)
+ {
+ $values = [];
+ $uri = $request->getUri();
+ $scheme = $uri->getScheme();
+ $host = $uri->getHost();
+ $path = $uri->getPath() ?: '/';
+
+ foreach ($this->cookies as $cookie) {
+ if ($cookie->matchesPath($path) &&
+ $cookie->matchesDomain($host) &&
+ !$cookie->isExpired() &&
+ (!$cookie->getSecure() || $scheme === 'https')
+ ) {
+ $values[] = $cookie->getName() . '='
+ . $cookie->getValue();
+ }
+ }
+
+ return $values
+ ? $request->withHeader('Cookie', implode('; ', $values))
+ : $request;
+ }
+
+ /**
+ * If a cookie already exists and the server asks to set it again with a
+ * null value, the cookie must be deleted.
+ *
+ * @param SetCookie $cookie
+ */
+ private function removeCookieIfEmpty(SetCookie $cookie)
+ {
+ $cookieValue = $cookie->getValue();
+ if ($cookieValue === null || $cookieValue === '') {
+ $this->clear(
+ $cookie->getDomain(),
+ $cookie->getPath(),
+ $cookie->getName()
+ );
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
new file mode 100644
index 00000000..2cf298a8
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php
@@ -0,0 +1,84 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Stores HTTP cookies.
+ *
+ * It extracts cookies from HTTP requests, and returns them in HTTP responses.
+ * CookieJarInterface instances automatically expire contained cookies when
+ * necessary. Subclasses are also responsible for storing and retrieving
+ * cookies from a file, database, etc.
+ *
+ * @link http://docs.python.org/2/library/cookielib.html Inspiration
+ */
+interface CookieJarInterface extends \Countable, \IteratorAggregate
+{
+ /**
+ * Create a request with added cookie headers.
+ *
+ * If no matching cookies are found in the cookie jar, then no Cookie
+ * header is added to the request and the same request is returned.
+ *
+ * @param RequestInterface $request Request object to modify.
+ *
+ * @return RequestInterface returns the modified request.
+ */
+ public function withCookieHeader(RequestInterface $request);
+
+ /**
+ * Extract cookies from an HTTP response and store them in the CookieJar.
+ *
+ * @param RequestInterface $request Request that was sent
+ * @param ResponseInterface $response Response that was received
+ */
+ public function extractCookies(
+ RequestInterface $request,
+ ResponseInterface $response
+ );
+
+ /**
+ * Sets a cookie in the cookie jar.
+ *
+ * @param SetCookie $cookie Cookie to set.
+ *
+ * @return bool Returns true on success or false on failure
+ */
+ public function setCookie(SetCookie $cookie);
+
+ /**
+ * Remove cookies currently held in the cookie jar.
+ *
+ * Invoking this method without arguments will empty the whole cookie jar.
+ * If given a $domain argument only cookies belonging to that domain will
+ * be removed. If given a $domain and $path argument, cookies belonging to
+ * the specified path within that domain are removed. If given all three
+ * arguments, then the cookie with the specified name, path and domain is
+ * removed.
+ *
+ * @param string $domain Clears cookies matching a domain
+ * @param string $path Clears cookies matching a domain and path
+ * @param string $name Clears cookies matching a domain, path, and name
+ *
+ * @return CookieJarInterface
+ */
+ public function clear($domain = null, $path = null, $name = null);
+
+ /**
+ * Discard all sessions cookies.
+ *
+ * Removes cookies that don't have an expire field or a have a discard
+ * field set to true. To be called when the user agent shuts down according
+ * to RFC 2965.
+ */
+ public function clearSessionCookies();
+
+ /**
+ * Converts the cookie jar to an array.
+ *
+ * @return array
+ */
+ public function toArray();
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
new file mode 100644
index 00000000..9887c1d5
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php
@@ -0,0 +1,90 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+/**
+ * Persists non-session cookies using a JSON formatted file
+ */
+class FileCookieJar extends CookieJar
+{
+ /** @var string filename */
+ private $filename;
+
+ /** @var bool Control whether to persist session cookies or not. */
+ private $storeSessionCookies;
+
+ /**
+ * Create a new FileCookieJar object
+ *
+ * @param string $cookieFile File to store the cookie data
+ * @param bool $storeSessionCookies Set to true to store session cookies
+ * in the cookie jar.
+ *
+ * @throws \RuntimeException if the file cannot be found or created
+ */
+ public function __construct($cookieFile, $storeSessionCookies = false)
+ {
+ $this->filename = $cookieFile;
+ $this->storeSessionCookies = $storeSessionCookies;
+
+ if (file_exists($cookieFile)) {
+ $this->load($cookieFile);
+ }
+ }
+
+ /**
+ * Saves the file when shutting down
+ */
+ public function __destruct()
+ {
+ $this->save($this->filename);
+ }
+
+ /**
+ * Saves the cookies to a file.
+ *
+ * @param string $filename File to save
+ * @throws \RuntimeException if the file cannot be found or created
+ */
+ public function save($filename)
+ {
+ $json = [];
+ foreach ($this as $cookie) {
+ /** @var SetCookie $cookie */
+ if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
+ $json[] = $cookie->toArray();
+ }
+ }
+
+ $jsonStr = \GuzzleHttp\json_encode($json);
+ if (false === file_put_contents($filename, $jsonStr)) {
+ throw new \RuntimeException("Unable to save file {$filename}");
+ }
+ }
+
+ /**
+ * Load cookies from a JSON formatted file.
+ *
+ * Old cookies are kept unless overwritten by newly loaded ones.
+ *
+ * @param string $filename Cookie file to load.
+ * @throws \RuntimeException if the file cannot be loaded.
+ */
+ public function load($filename)
+ {
+ $json = file_get_contents($filename);
+ if (false === $json) {
+ throw new \RuntimeException("Unable to load file {$filename}");
+ } elseif ($json === '') {
+ return;
+ }
+
+ $data = \GuzzleHttp\json_decode($json, true);
+ if (is_array($data)) {
+ foreach (json_decode($json, true) as $cookie) {
+ $this->setCookie(new SetCookie($cookie));
+ }
+ } elseif (strlen($data)) {
+ throw new \RuntimeException("Invalid cookie file: {$filename}");
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
new file mode 100644
index 00000000..4497bcf0
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php
@@ -0,0 +1,71 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+/**
+ * Persists cookies in the client session
+ */
+class SessionCookieJar extends CookieJar
+{
+ /** @var string session key */
+ private $sessionKey;
+
+ /** @var bool Control whether to persist session cookies or not. */
+ private $storeSessionCookies;
+
+ /**
+ * Create a new SessionCookieJar object
+ *
+ * @param string $sessionKey Session key name to store the cookie
+ * data in session
+ * @param bool $storeSessionCookies Set to true to store session cookies
+ * in the cookie jar.
+ */
+ public function __construct($sessionKey, $storeSessionCookies = false)
+ {
+ $this->sessionKey = $sessionKey;
+ $this->storeSessionCookies = $storeSessionCookies;
+ $this->load();
+ }
+
+ /**
+ * Saves cookies to session when shutting down
+ */
+ public function __destruct()
+ {
+ $this->save();
+ }
+
+ /**
+ * Save cookies to the client session
+ */
+ public function save()
+ {
+ $json = [];
+ foreach ($this as $cookie) {
+ /** @var SetCookie $cookie */
+ if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
+ $json[] = $cookie->toArray();
+ }
+ }
+
+ $_SESSION[$this->sessionKey] = json_encode($json);
+ }
+
+ /**
+ * Load the contents of the client session into the data array
+ */
+ protected function load()
+ {
+ if (!isset($_SESSION[$this->sessionKey])) {
+ return;
+ }
+ $data = json_decode($_SESSION[$this->sessionKey], true);
+ if (is_array($data)) {
+ foreach ($data as $cookie) {
+ $this->setCookie(new SetCookie($cookie));
+ }
+ } elseif (strlen($data)) {
+ throw new \RuntimeException("Invalid cookie data");
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
new file mode 100644
index 00000000..f6993943
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php
@@ -0,0 +1,403 @@
+<?php
+namespace GuzzleHttp\Cookie;
+
+/**
+ * Set-Cookie object
+ */
+class SetCookie
+{
+ /** @var array */
+ private static $defaults = [
+ 'Name' => null,
+ 'Value' => null,
+ 'Domain' => null,
+ 'Path' => '/',
+ 'Max-Age' => null,
+ 'Expires' => null,
+ 'Secure' => false,
+ 'Discard' => false,
+ 'HttpOnly' => false
+ ];
+
+ /** @var array Cookie data */
+ private $data;
+
+ /**
+ * Create a new SetCookie object from a string
+ *
+ * @param string $cookie Set-Cookie header string
+ *
+ * @return self
+ */
+ public static function fromString($cookie)
+ {
+ // Create the default return array
+ $data = self::$defaults;
+ // Explode the cookie string using a series of semicolons
+ $pieces = array_filter(array_map('trim', explode(';', $cookie)));
+ // The name of the cookie (first kvp) must exist and include an equal sign.
+ if (empty($pieces[0]) || !strpos($pieces[0], '=')) {
+ return new self($data);
+ }
+
+ // Add the cookie pieces into the parsed data array
+ foreach ($pieces as $part) {
+ $cookieParts = explode('=', $part, 2);
+ $key = trim($cookieParts[0]);
+ $value = isset($cookieParts[1])
+ ? trim($cookieParts[1], " \n\r\t\0\x0B")
+ : true;
+
+ // Only check for non-cookies when cookies have been found
+ if (empty($data['Name'])) {
+ $data['Name'] = $key;
+ $data['Value'] = $value;
+ } else {
+ foreach (array_keys(self::$defaults) as $search) {
+ if (!strcasecmp($search, $key)) {
+ $data[$search] = $value;
+ continue 2;
+ }
+ }
+ $data[$key] = $value;
+ }
+ }
+
+ return new self($data);
+ }
+
+ /**
+ * @param array $data Array of cookie data provided by a Cookie parser
+ */
+ public function __construct(array $data = [])
+ {
+ $this->data = array_replace(self::$defaults, $data);
+ // Extract the Expires value and turn it into a UNIX timestamp if needed
+ if (!$this->getExpires() && $this->getMaxAge()) {
+ // Calculate the Expires date
+ $this->setExpires(time() + $this->getMaxAge());
+ } elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
+ $this->setExpires($this->getExpires());
+ }
+ }
+
+ public function __toString()
+ {
+ $str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
+ foreach ($this->data as $k => $v) {
+ if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) {
+ if ($k === 'Expires') {
+ $str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
+ } else {
+ $str .= ($v === true ? $k : "{$k}={$v}") . '; ';
+ }
+ }
+ }
+
+ return rtrim($str, '; ');
+ }
+
+ public function toArray()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Get the cookie name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->data['Name'];
+ }
+
+ /**
+ * Set the cookie name
+ *
+ * @param string $name Cookie name
+ */
+ public function setName($name)
+ {
+ $this->data['Name'] = $name;
+ }
+
+ /**
+ * Get the cookie value
+ *
+ * @return string
+ */
+ public function getValue()
+ {
+ return $this->data['Value'];
+ }
+
+ /**
+ * Set the cookie value
+ *
+ * @param string $value Cookie value
+ */
+ public function setValue($value)
+ {
+ $this->data['Value'] = $value;
+ }
+
+ /**
+ * Get the domain
+ *
+ * @return string|null
+ */
+ public function getDomain()
+ {
+ return $this->data['Domain'];
+ }
+
+ /**
+ * Set the domain of the cookie
+ *
+ * @param string $domain
+ */
+ public function setDomain($domain)
+ {
+ $this->data['Domain'] = $domain;
+ }
+
+ /**
+ * Get the path
+ *
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->data['Path'];
+ }
+
+ /**
+ * Set the path of the cookie
+ *
+ * @param string $path Path of the cookie
+ */
+ public function setPath($path)
+ {
+ $this->data['Path'] = $path;
+ }
+
+ /**
+ * Maximum lifetime of the cookie in seconds
+ *
+ * @return int|null
+ */
+ public function getMaxAge()
+ {
+ return $this->data['Max-Age'];
+ }
+
+ /**
+ * Set the max-age of the cookie
+ *
+ * @param int $maxAge Max age of the cookie in seconds
+ */
+ public function setMaxAge($maxAge)
+ {
+ $this->data['Max-Age'] = $maxAge;
+ }
+
+ /**
+ * The UNIX timestamp when the cookie Expires
+ *
+ * @return mixed
+ */
+ public function getExpires()
+ {
+ return $this->data['Expires'];
+ }
+
+ /**
+ * Set the unix timestamp for which the cookie will expire
+ *
+ * @param int $timestamp Unix timestamp
+ */
+ public function setExpires($timestamp)
+ {
+ $this->data['Expires'] = is_numeric($timestamp)
+ ? (int) $timestamp
+ : strtotime($timestamp);
+ }
+
+ /**
+ * Get whether or not this is a secure cookie
+ *
+ * @return null|bool
+ */
+ public function getSecure()
+ {
+ return $this->data['Secure'];
+ }
+
+ /**
+ * Set whether or not the cookie is secure
+ *
+ * @param bool $secure Set to true or false if secure
+ */
+ public function setSecure($secure)
+ {
+ $this->data['Secure'] = $secure;
+ }
+
+ /**
+ * Get whether or not this is a session cookie
+ *
+ * @return null|bool
+ */
+ public function getDiscard()
+ {
+ return $this->data['Discard'];
+ }
+
+ /**
+ * Set whether or not this is a session cookie
+ *
+ * @param bool $discard Set to true or false if this is a session cookie
+ */
+ public function setDiscard($discard)
+ {
+ $this->data['Discard'] = $discard;
+ }
+
+ /**
+ * Get whether or not this is an HTTP only cookie
+ *
+ * @return bool
+ */
+ public function getHttpOnly()
+ {
+ return $this->data['HttpOnly'];
+ }
+
+ /**
+ * Set whether or not this is an HTTP only cookie
+ *
+ * @param bool $httpOnly Set to true or false if this is HTTP only
+ */
+ public function setHttpOnly($httpOnly)
+ {
+ $this->data['HttpOnly'] = $httpOnly;
+ }
+
+ /**
+ * Check if the cookie matches a path value.
+ *
+ * A request-path path-matches a given cookie-path if at least one of
+ * the following conditions holds:
+ *
+ * - The cookie-path and the request-path are identical.
+ * - The cookie-path is a prefix of the request-path, and the last
+ * character of the cookie-path is %x2F ("/").
+ * - The cookie-path is a prefix of the request-path, and the first
+ * character of the request-path that is not included in the cookie-
+ * path is a %x2F ("/") character.
+ *
+ * @param string $requestPath Path to check against
+ *
+ * @return bool
+ */
+ public function matchesPath($requestPath)
+ {
+ $cookiePath = $this->getPath();
+
+ // Match on exact matches or when path is the default empty "/"
+ if ($cookiePath === '/' || $cookiePath == $requestPath) {
+ return true;
+ }
+
+ // Ensure that the cookie-path is a prefix of the request path.
+ if (0 !== strpos($requestPath, $cookiePath)) {
+ return false;
+ }
+
+ // Match if the last character of the cookie-path is "/"
+ if (substr($cookiePath, -1, 1) === '/') {
+ return true;
+ }
+
+ // Match if the first character not included in cookie path is "/"
+ return substr($requestPath, strlen($cookiePath), 1) === '/';
+ }
+
+ /**
+ * Check if the cookie matches a domain value
+ *
+ * @param string $domain Domain to check against
+ *
+ * @return bool
+ */
+ public function matchesDomain($domain)
+ {
+ // Remove the leading '.' as per spec in RFC 6265.
+ // http://tools.ietf.org/html/rfc6265#section-5.2.3
+ $cookieDomain = ltrim($this->getDomain(), '.');
+
+ // Domain not set or exact match.
+ if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
+ return true;
+ }
+
+ // Matching the subdomain according to RFC 6265.
+ // http://tools.ietf.org/html/rfc6265#section-5.1.3
+ if (filter_var($domain, FILTER_VALIDATE_IP)) {
+ return false;
+ }
+
+ return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/', $domain);
+ }
+
+ /**
+ * Check if the cookie is expired
+ *
+ * @return bool
+ */
+ public function isExpired()
+ {
+ return $this->getExpires() !== null && time() > $this->getExpires();
+ }
+
+ /**
+ * Check if the cookie is valid according to RFC 6265
+ *
+ * @return bool|string Returns true if valid or an error message if invalid
+ */
+ public function validate()
+ {
+ // Names must not be empty, but can be 0
+ $name = $this->getName();
+ if (empty($name) && !is_numeric($name)) {
+ return 'The cookie name must not be empty';
+ }
+
+ // Check if any of the invalid characters are present in the cookie name
+ if (preg_match(
+ '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
+ $name
+ )) {
+ return 'Cookie name must not contain invalid characters: ASCII '
+ . 'Control characters (0-31;127), space, tab and the '
+ . 'following characters: ()<>@,;:\"/?={}';
+ }
+
+ // Value must not be empty, but can be 0
+ $value = $this->getValue();
+ if (empty($value) && !is_numeric($value)) {
+ return 'The cookie value must not be empty';
+ }
+
+ // Domains must not be empty, but can be 0
+ // A "0" is not a valid internet domain, but may be used as server name
+ // in a private network.
+ $domain = $this->getDomain();
+ if (empty($domain) && !is_numeric($domain)) {
+ return 'The cookie domain must not be empty';
+ }
+
+ return true;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
new file mode 100644
index 00000000..427d896f
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/BadResponseException.php
@@ -0,0 +1,27 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Exception when an HTTP error occurs (4xx or 5xx error)
+ */
+class BadResponseException extends RequestException
+{
+ public function __construct(
+ $message,
+ RequestInterface $request,
+ ResponseInterface $response = null,
+ \Exception $previous = null,
+ array $handlerContext = []
+ ) {
+ if (null === $response) {
+ @trigger_error(
+ 'Instantiating the ' . __CLASS__ . ' class without a Response is deprecated since version 6.3 and will be removed in 7.0.',
+ E_USER_DEPRECATED
+ );
+ }
+ parent::__construct($message, $request, $response, $previous, $handlerContext);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php
new file mode 100644
index 00000000..f95c09f2
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ClientException.php
@@ -0,0 +1,7 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+/**
+ * Exception when a client error is encountered (4xx codes)
+ */
+class ClientException extends BadResponseException {}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
new file mode 100644
index 00000000..d33b0cc1
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ConnectException.php
@@ -0,0 +1,37 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Exception thrown when a connection cannot be established.
+ *
+ * Note that no response is present for a ConnectException
+ */
+class ConnectException extends RequestException
+{
+ public function __construct(
+ $message,
+ RequestInterface $request,
+ \Exception $previous = null,
+ array $handlerContext = []
+ ) {
+ parent::__construct($message, $request, null, $previous, $handlerContext);
+ }
+
+ /**
+ * @return null
+ */
+ public function getResponse()
+ {
+ return null;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasResponse()
+ {
+ return false;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php
new file mode 100644
index 00000000..510778f6
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/GuzzleException.php
@@ -0,0 +1,13 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+/**
+ * @method string getMessage()
+ * @method \Throwable|null getPrevious()
+ * @method mixed getCode()
+ * @method string getFile()
+ * @method int getLine()
+ * @method array getTrace()
+ * @method string getTraceAsString()
+ */
+interface GuzzleException {}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
new file mode 100644
index 00000000..39de327e
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php
@@ -0,0 +1,217 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use GuzzleHttp\Promise\PromiseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * HTTP Request exception
+ */
+class RequestException extends TransferException
+{
+ /** @var RequestInterface */
+ private $request;
+
+ /** @var ResponseInterface */
+ private $response;
+
+ /** @var array */
+ private $handlerContext;
+
+ public function __construct(
+ $message,
+ RequestInterface $request,
+ ResponseInterface $response = null,
+ \Exception $previous = null,
+ array $handlerContext = []
+ ) {
+ // Set the code of the exception if the response is set and not future.
+ $code = $response && !($response instanceof PromiseInterface)
+ ? $response->getStatusCode()
+ : 0;
+ parent::__construct($message, $code, $previous);
+ $this->request = $request;
+ $this->response = $response;
+ $this->handlerContext = $handlerContext;
+ }
+
+ /**
+ * Wrap non-RequestExceptions with a RequestException
+ *
+ * @param RequestInterface $request
+ * @param \Exception $e
+ *
+ * @return RequestException
+ */
+ public static function wrapException(RequestInterface $request, \Exception $e)
+ {
+ return $e instanceof RequestException
+ ? $e
+ : new RequestException($e->getMessage(), $request, null, $e);
+ }
+
+ /**
+ * Factory method to create a new exception with a normalized error message
+ *
+ * @param RequestInterface $request Request
+ * @param ResponseInterface $response Response received
+ * @param \Exception $previous Previous exception
+ * @param array $ctx Optional handler context.
+ *
+ * @return self
+ */
+ public static function create(
+ RequestInterface $request,
+ ResponseInterface $response = null,
+ \Exception $previous = null,
+ array $ctx = []
+ ) {
+ if (!$response) {
+ return new self(
+ 'Error completing request',
+ $request,
+ null,
+ $previous,
+ $ctx
+ );
+ }
+
+ $level = (int) floor($response->getStatusCode() / 100);
+ if ($level === 4) {
+ $label = 'Client error';
+ $className = ClientException::class;
+ } elseif ($level === 5) {
+ $label = 'Server error';
+ $className = ServerException::class;
+ } else {
+ $label = 'Unsuccessful request';
+ $className = __CLASS__;
+ }
+
+ $uri = $request->getUri();
+ $uri = static::obfuscateUri($uri);
+
+ // Client Error: `GET /` resulted in a `404 Not Found` response:
+ // <html> ... (truncated)
+ $message = sprintf(
+ '%s: `%s %s` resulted in a `%s %s` response',
+ $label,
+ $request->getMethod(),
+ $uri,
+ $response->getStatusCode(),
+ $response->getReasonPhrase()
+ );
+
+ $summary = static::getResponseBodySummary($response);
+
+ if ($summary !== null) {
+ $message .= ":\n{$summary}\n";
+ }
+
+ return new $className($message, $request, $response, $previous, $ctx);
+ }
+
+ /**
+ * Get a short summary of the response
+ *
+ * Will return `null` if the response is not printable.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return string|null
+ */
+ public static function getResponseBodySummary(ResponseInterface $response)
+ {
+ $body = $response->getBody();
+
+ if (!$body->isSeekable()) {
+ return null;
+ }
+
+ $size = $body->getSize();
+
+ if ($size === 0) {
+ return null;
+ }
+
+ $summary = $body->read(120);
+ $body->rewind();
+
+ if ($size > 120) {
+ $summary .= ' (truncated...)';
+ }
+
+ // Matches any printable character, including unicode characters:
+ // letters, marks, numbers, punctuation, spacing, and separators.
+ if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
+ return null;
+ }
+
+ return $summary;
+ }
+
+ /**
+ * Obfuscates URI if there is an username and a password present
+ *
+ * @param UriInterface $uri
+ *
+ * @return UriInterface
+ */
+ private static function obfuscateUri($uri)
+ {
+ $userInfo = $uri->getUserInfo();
+
+ if (false !== ($pos = strpos($userInfo, ':'))) {
+ return $uri->withUserInfo(substr($userInfo, 0, $pos), '***');
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Get the request that caused the exception
+ *
+ * @return RequestInterface
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ /**
+ * Get the associated response
+ *
+ * @return ResponseInterface|null
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Check if a response was received
+ *
+ * @return bool
+ */
+ public function hasResponse()
+ {
+ return $this->response !== null;
+ }
+
+ /**
+ * Get contextual information about the error from the underlying handler.
+ *
+ * The contents of this array will vary depending on which handler you are
+ * using. It may also be just an empty array. Relying on this data will
+ * couple you to a specific handler, but can give more debug information
+ * when needed.
+ *
+ * @return array
+ */
+ public function getHandlerContext()
+ {
+ return $this->handlerContext;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php
new file mode 100644
index 00000000..a77c2892
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/SeekException.php
@@ -0,0 +1,27 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Exception thrown when a seek fails on a stream.
+ */
+class SeekException extends \RuntimeException implements GuzzleException
+{
+ private $stream;
+
+ public function __construct(StreamInterface $stream, $pos = 0, $msg = '')
+ {
+ $this->stream = $stream;
+ $msg = $msg ?: 'Could not seek the stream to position ' . $pos;
+ parent::__construct($msg);
+ }
+
+ /**
+ * @return StreamInterface
+ */
+ public function getStream()
+ {
+ return $this->stream;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php
new file mode 100644
index 00000000..7cdd3408
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/ServerException.php
@@ -0,0 +1,7 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+/**
+ * Exception when a server error is encountered (5xx codes)
+ */
+class ServerException extends BadResponseException {}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php
new file mode 100644
index 00000000..b60a9678
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php
@@ -0,0 +1,4 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+class TooManyRedirectsException extends RequestException {}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php
new file mode 100644
index 00000000..b92071ca
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Exception/TransferException.php
@@ -0,0 +1,4 @@
+<?php
+namespace GuzzleHttp\Exception;
+
+class TransferException extends \RuntimeException implements GuzzleException {}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
new file mode 100644
index 00000000..e0923714
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php
@@ -0,0 +1,565 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Promise\FulfilledPromise;
+use GuzzleHttp\Psr7;
+use GuzzleHttp\Psr7\LazyOpenStream;
+use GuzzleHttp\TransferStats;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Creates curl resources from a request
+ */
+class CurlFactory implements CurlFactoryInterface
+{
+ /** @var array */
+ private $handles = [];
+
+ /** @var int Total number of idle handles to keep in cache */
+ private $maxHandles;
+
+ /**
+ * @param int $maxHandles Maximum number of idle handles.
+ */
+ public function __construct($maxHandles)
+ {
+ $this->maxHandles = $maxHandles;
+ }
+
+ public function create(RequestInterface $request, array $options)
+ {
+ if (isset($options['curl']['body_as_string'])) {
+ $options['_body_as_string'] = $options['curl']['body_as_string'];
+ unset($options['curl']['body_as_string']);
+ }
+
+ $easy = new EasyHandle;
+ $easy->request = $request;
+ $easy->options = $options;
+ $conf = $this->getDefaultConf($easy);
+ $this->applyMethod($easy, $conf);
+ $this->applyHandlerOptions($easy, $conf);
+ $this->applyHeaders($easy, $conf);
+ unset($conf['_headers']);
+
+ // Add handler options from the request configuration options
+ if (isset($options['curl'])) {
+ $conf = array_replace($conf, $options['curl']);
+ }
+
+ $conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
+ $easy->handle = $this->handles
+ ? array_pop($this->handles)
+ : curl_init();
+ curl_setopt_array($easy->handle, $conf);
+
+ return $easy;
+ }
+
+ public function release(EasyHandle $easy)
+ {
+ $resource = $easy->handle;
+ unset($easy->handle);
+
+ if (count($this->handles) >= $this->maxHandles) {
+ curl_close($resource);
+ } else {
+ // Remove all callback functions as they can hold onto references
+ // and are not cleaned up by curl_reset. Using curl_setopt_array
+ // does not work for some reason, so removing each one
+ // individually.
+ curl_setopt($resource, CURLOPT_HEADERFUNCTION, null);
+ curl_setopt($resource, CURLOPT_READFUNCTION, null);
+ curl_setopt($resource, CURLOPT_WRITEFUNCTION, null);
+ curl_setopt($resource, CURLOPT_PROGRESSFUNCTION, null);
+ curl_reset($resource);
+ $this->handles[] = $resource;
+ }
+ }
+
+ /**
+ * Completes a cURL transaction, either returning a response promise or a
+ * rejected promise.
+ *
+ * @param callable $handler
+ * @param EasyHandle $easy
+ * @param CurlFactoryInterface $factory Dictates how the handle is released
+ *
+ * @return \GuzzleHttp\Promise\PromiseInterface
+ */
+ public static function finish(
+ callable $handler,
+ EasyHandle $easy,
+ CurlFactoryInterface $factory
+ ) {
+ if (isset($easy->options['on_stats'])) {
+ self::invokeStats($easy);
+ }
+
+ if (!$easy->response || $easy->errno) {
+ return self::finishError($handler, $easy, $factory);
+ }
+
+ // Return the response if it is present and there is no error.
+ $factory->release($easy);
+
+ // Rewind the body of the response if possible.
+ $body = $easy->response->getBody();
+ if ($body->isSeekable()) {
+ $body->rewind();
+ }
+
+ return new FulfilledPromise($easy->response);
+ }
+
+ private static function invokeStats(EasyHandle $easy)
+ {
+ $curlStats = curl_getinfo($easy->handle);
+ $stats = new TransferStats(
+ $easy->request,
+ $easy->response,
+ $curlStats['total_time'],
+ $easy->errno,
+ $curlStats
+ );
+ call_user_func($easy->options['on_stats'], $stats);
+ }
+
+ private static function finishError(
+ callable $handler,
+ EasyHandle $easy,
+ CurlFactoryInterface $factory
+ ) {
+ // Get error information and release the handle to the factory.
+ $ctx = [
+ 'errno' => $easy->errno,
+ 'error' => curl_error($easy->handle),
+ ] + curl_getinfo($easy->handle);
+ $factory->release($easy);
+
+ // Retry when nothing is present or when curl failed to rewind.
+ if (empty($easy->options['_err_message'])
+ && (!$easy->errno || $easy->errno == 65)
+ ) {
+ return self::retryFailedRewind($handler, $easy, $ctx);
+ }
+
+ return self::createRejection($easy, $ctx);
+ }
+
+ private static function createRejection(EasyHandle $easy, array $ctx)
+ {
+ static $connectionErrors = [
+ CURLE_OPERATION_TIMEOUTED => true,
+ CURLE_COULDNT_RESOLVE_HOST => true,
+ CURLE_COULDNT_CONNECT => true,
+ CURLE_SSL_CONNECT_ERROR => true,
+ CURLE_GOT_NOTHING => true,
+ ];
+
+ // If an exception was encountered during the onHeaders event, then
+ // return a rejected promise that wraps that exception.
+ if ($easy->onHeadersException) {
+ return \GuzzleHttp\Promise\rejection_for(
+ new RequestException(
+ 'An error was encountered during the on_headers event',
+ $easy->request,
+ $easy->response,
+ $easy->onHeadersException,
+ $ctx
+ )
+ );
+ }
+
+ $message = sprintf(
+ 'cURL error %s: %s (%s)',
+ $ctx['errno'],
+ $ctx['error'],
+ 'see http://curl.haxx.se/libcurl/c/libcurl-errors.html'
+ );
+
+ // Create a connection exception if it was a specific error code.
+ $error = isset($connectionErrors[$easy->errno])
+ ? new ConnectException($message, $easy->request, null, $ctx)
+ : new RequestException($message, $easy->request, $easy->response, null, $ctx);
+
+ return \GuzzleHttp\Promise\rejection_for($error);
+ }
+
+ private function getDefaultConf(EasyHandle $easy)
+ {
+ $conf = [
+ '_headers' => $easy->request->getHeaders(),
+ CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
+ CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''),
+ CURLOPT_RETURNTRANSFER => false,
+ CURLOPT_HEADER => false,
+ CURLOPT_CONNECTTIMEOUT => 150,
+ ];
+
+ if (defined('CURLOPT_PROTOCOLS')) {
+ $conf[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
+ }
+
+ $version = $easy->request->getProtocolVersion();
+ if ($version == 1.1) {
+ $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_1;
+ } elseif ($version == 2.0) {
+ $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2_0;
+ } else {
+ $conf[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
+ }
+
+ return $conf;
+ }
+
+ private function applyMethod(EasyHandle $easy, array &$conf)
+ {
+ $body = $easy->request->getBody();
+ $size = $body->getSize();
+
+ if ($size === null || $size > 0) {
+ $this->applyBody($easy->request, $easy->options, $conf);
+ return;
+ }
+
+ $method = $easy->request->getMethod();
+ if ($method === 'PUT' || $method === 'POST') {
+ // See http://tools.ietf.org/html/rfc7230#section-3.3.2
+ if (!$easy->request->hasHeader('Content-Length')) {
+ $conf[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
+ }
+ } elseif ($method === 'HEAD') {
+ $conf[CURLOPT_NOBODY] = true;
+ unset(
+ $conf[CURLOPT_WRITEFUNCTION],
+ $conf[CURLOPT_READFUNCTION],
+ $conf[CURLOPT_FILE],
+ $conf[CURLOPT_INFILE]
+ );
+ }
+ }
+
+ private function applyBody(RequestInterface $request, array $options, array &$conf)
+ {
+ $size = $request->hasHeader('Content-Length')
+ ? (int) $request->getHeaderLine('Content-Length')
+ : null;
+
+ // Send the body as a string if the size is less than 1MB OR if the
+ // [curl][body_as_string] request value is set.
+ if (($size !== null && $size < 1000000) ||
+ !empty($options['_body_as_string'])
+ ) {
+ $conf[CURLOPT_POSTFIELDS] = (string) $request->getBody();
+ // Don't duplicate the Content-Length header
+ $this->removeHeader('Content-Length', $conf);
+ $this->removeHeader('Transfer-Encoding', $conf);
+ } else {
+ $conf[CURLOPT_UPLOAD] = true;
+ if ($size !== null) {
+ $conf[CURLOPT_INFILESIZE] = $size;
+ $this->removeHeader('Content-Length', $conf);
+ }
+ $body = $request->getBody();
+ if ($body->isSeekable()) {
+ $body->rewind();
+ }
+ $conf[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
+ return $body->read($length);
+ };
+ }
+
+ // If the Expect header is not present, prevent curl from adding it
+ if (!$request->hasHeader('Expect')) {
+ $conf[CURLOPT_HTTPHEADER][] = 'Expect:';
+ }
+
+ // cURL sometimes adds a content-type by default. Prevent this.
+ if (!$request->hasHeader('Content-Type')) {
+ $conf[CURLOPT_HTTPHEADER][] = 'Content-Type:';
+ }
+ }
+
+ private function applyHeaders(EasyHandle $easy, array &$conf)
+ {
+ foreach ($conf['_headers'] as $name => $values) {
+ foreach ($values as $value) {
+ $value = (string) $value;
+ if ($value === '') {
+ // cURL requires a special format for empty headers.
+ // See https://github.com/guzzle/guzzle/issues/1882 for more details.
+ $conf[CURLOPT_HTTPHEADER][] = "$name;";
+ } else {
+ $conf[CURLOPT_HTTPHEADER][] = "$name: $value";
+ }
+ }
+ }
+
+ // Remove the Accept header if one was not set
+ if (!$easy->request->hasHeader('Accept')) {
+ $conf[CURLOPT_HTTPHEADER][] = 'Accept:';
+ }
+ }
+
+ /**
+ * Remove a header from the options array.
+ *
+ * @param string $name Case-insensitive header to remove
+ * @param array $options Array of options to modify
+ */
+ private function removeHeader($name, array &$options)
+ {
+ foreach (array_keys($options['_headers']) as $key) {
+ if (!strcasecmp($key, $name)) {
+ unset($options['_headers'][$key]);
+ return;
+ }
+ }
+ }
+
+ private function applyHandlerOptions(EasyHandle $easy, array &$conf)
+ {
+ $options = $easy->options;
+ if (isset($options['verify'])) {
+ if ($options['verify'] === false) {
+ unset($conf[CURLOPT_CAINFO]);
+ $conf[CURLOPT_SSL_VERIFYHOST] = 0;
+ $conf[CURLOPT_SSL_VERIFYPEER] = false;
+ } else {
+ $conf[CURLOPT_SSL_VERIFYHOST] = 2;
+ $conf[CURLOPT_SSL_VERIFYPEER] = true;
+ if (is_string($options['verify'])) {
+ // Throw an error if the file/folder/link path is not valid or doesn't exist.
+ if (!file_exists($options['verify'])) {
+ throw new \InvalidArgumentException(
+ "SSL CA bundle not found: {$options['verify']}"
+ );
+ }
+ // If it's a directory or a link to a directory use CURLOPT_CAPATH.
+ // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO.
+ if (is_dir($options['verify']) ||
+ (is_link($options['verify']) && is_dir(readlink($options['verify'])))) {
+ $conf[CURLOPT_CAPATH] = $options['verify'];
+ } else {
+ $conf[CURLOPT_CAINFO] = $options['verify'];
+ }
+ }
+ }
+ }
+
+ if (!empty($options['decode_content'])) {
+ $accept = $easy->request->getHeaderLine('Accept-Encoding');
+ if ($accept) {
+ $conf[CURLOPT_ENCODING] = $accept;
+ } else {
+ $conf[CURLOPT_ENCODING] = '';
+ // Don't let curl send the header over the wire
+ $conf[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
+ }
+ }
+
+ if (isset($options['sink'])) {
+ $sink = $options['sink'];
+ if (!is_string($sink)) {
+ $sink = \GuzzleHttp\Psr7\stream_for($sink);
+ } elseif (!is_dir(dirname($sink))) {
+ // Ensure that the directory exists before failing in curl.
+ throw new \RuntimeException(sprintf(
+ 'Directory %s does not exist for sink value of %s',
+ dirname($sink),
+ $sink
+ ));
+ } else {
+ $sink = new LazyOpenStream($sink, 'w+');
+ }
+ $easy->sink = $sink;
+ $conf[CURLOPT_WRITEFUNCTION] = function ($ch, $write) use ($sink) {
+ return $sink->write($write);
+ };
+ } else {
+ // Use a default temp stream if no sink was set.
+ $conf[CURLOPT_FILE] = fopen('php://temp', 'w+');
+ $easy->sink = Psr7\stream_for($conf[CURLOPT_FILE]);
+ }
+ $timeoutRequiresNoSignal = false;
+ if (isset($options['timeout'])) {
+ $timeoutRequiresNoSignal |= $options['timeout'] < 1;
+ $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
+ }
+
+ // CURL default value is CURL_IPRESOLVE_WHATEVER
+ if (isset($options['force_ip_resolve'])) {
+ if ('v4' === $options['force_ip_resolve']) {
+ $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
+ } elseif ('v6' === $options['force_ip_resolve']) {
+ $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
+ }
+ }
+
+ if (isset($options['connect_timeout'])) {
+ $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1;
+ $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
+ }
+
+ if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
+ $conf[CURLOPT_NOSIGNAL] = true;
+ }
+
+ if (isset($options['proxy'])) {
+ if (!is_array($options['proxy'])) {
+ $conf[CURLOPT_PROXY] = $options['proxy'];
+ } else {
+ $scheme = $easy->request->getUri()->getScheme();
+ if (isset($options['proxy'][$scheme])) {
+ $host = $easy->request->getUri()->getHost();
+ if (!isset($options['proxy']['no']) ||
+ !\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
+ ) {
+ $conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
+ }
+ }
+ }
+ }
+
+ if (isset($options['cert'])) {
+ $cert = $options['cert'];
+ if (is_array($cert)) {
+ $conf[CURLOPT_SSLCERTPASSWD] = $cert[1];
+ $cert = $cert[0];
+ }
+ if (!file_exists($cert)) {
+ throw new \InvalidArgumentException(
+ "SSL certificate not found: {$cert}"
+ );
+ }
+ $conf[CURLOPT_SSLCERT] = $cert;
+ }
+
+ if (isset($options['ssl_key'])) {
+ $sslKey = $options['ssl_key'];
+ if (is_array($sslKey)) {
+ $conf[CURLOPT_SSLKEYPASSWD] = $sslKey[1];
+ $sslKey = $sslKey[0];
+ }
+ if (!file_exists($sslKey)) {
+ throw new \InvalidArgumentException(
+ "SSL private key not found: {$sslKey}"
+ );
+ }
+ $conf[CURLOPT_SSLKEY] = $sslKey;
+ }
+
+ if (isset($options['progress'])) {
+ $progress = $options['progress'];
+ if (!is_callable($progress)) {
+ throw new \InvalidArgumentException(
+ 'progress client option must be callable'
+ );
+ }
+ $conf[CURLOPT_NOPROGRESS] = false;
+ $conf[CURLOPT_PROGRESSFUNCTION] = function () use ($progress) {
+ $args = func_get_args();
+ // PHP 5.5 pushed the handle onto the start of the args
+ if (is_resource($args[0])) {
+ array_shift($args);
+ }
+ call_user_func_array($progress, $args);
+ };
+ }
+
+ if (!empty($options['debug'])) {
+ $conf[CURLOPT_STDERR] = \GuzzleHttp\debug_resource($options['debug']);
+ $conf[CURLOPT_VERBOSE] = true;
+ }
+ }
+
+ /**
+ * This function ensures that a response was set on a transaction. If one
+ * was not set, then the request is retried if possible. This error
+ * typically means you are sending a payload, curl encountered a
+ * "Connection died, retrying a fresh connect" error, tried to rewind the
+ * stream, and then encountered a "necessary data rewind wasn't possible"
+ * error, causing the request to be sent through curl_multi_info_read()
+ * without an error status.
+ */
+ private static function retryFailedRewind(
+ callable $handler,
+ EasyHandle $easy,
+ array $ctx
+ ) {
+ try {
+ // Only rewind if the body has been read from.
+ $body = $easy->request->getBody();
+ if ($body->tell() > 0) {
+ $body->rewind();
+ }
+ } catch (\RuntimeException $e) {
+ $ctx['error'] = 'The connection unexpectedly failed without '
+ . 'providing an error. The request would have been retried, '
+ . 'but attempting to rewind the request body failed. '
+ . 'Exception: ' . $e;
+ return self::createRejection($easy, $ctx);
+ }
+
+ // Retry no more than 3 times before giving up.
+ if (!isset($easy->options['_curl_retries'])) {
+ $easy->options['_curl_retries'] = 1;
+ } elseif ($easy->options['_curl_retries'] == 2) {
+ $ctx['error'] = 'The cURL request was retried 3 times '
+ . 'and did not succeed. The most likely reason for the failure '
+ . 'is that cURL was unable to rewind the body of the request '
+ . 'and subsequent retries resulted in the same error. Turn on '
+ . 'the debug option to see what went wrong. See '
+ . 'https://bugs.php.net/bug.php?id=47204 for more information.';
+ return self::createRejection($easy, $ctx);
+ } else {
+ $easy->options['_curl_retries']++;
+ }
+
+ return $handler($easy->request, $easy->options);
+ }
+
+ private function createHeaderFn(EasyHandle $easy)
+ {
+ if (isset($easy->options['on_headers'])) {
+ $onHeaders = $easy->options['on_headers'];
+
+ if (!is_callable($onHeaders)) {
+ throw new \InvalidArgumentException('on_headers must be callable');
+ }
+ } else {
+ $onHeaders = null;
+ }
+
+ return function ($ch, $h) use (
+ $onHeaders,
+ $easy,
+ &$startingResponse
+ ) {
+ $value = trim($h);
+ if ($value === '') {
+ $startingResponse = true;
+ $easy->createResponse();
+ if ($onHeaders !== null) {
+ try {
+ $onHeaders($easy->response);
+ } catch (\Exception $e) {
+ // Associate the exception with the handle and trigger
+ // a curl header write error by returning 0.
+ $easy->onHeadersException = $e;
+ return -1;
+ }
+ }
+ } elseif ($startingResponse) {
+ $startingResponse = false;
+ $easy->headers = [$value];
+ } else {
+ $easy->headers[] = $value;
+ }
+ return strlen($h);
+ };
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php
new file mode 100644
index 00000000..b0fc2368
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php
@@ -0,0 +1,27 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use Psr\Http\Message\RequestInterface;
+
+interface CurlFactoryInterface
+{
+ /**
+ * Creates a cURL handle resource.
+ *
+ * @param RequestInterface $request Request
+ * @param array $options Transfer options
+ *
+ * @return EasyHandle
+ * @throws \RuntimeException when an option cannot be applied
+ */
+ public function create(RequestInterface $request, array $options);
+
+ /**
+ * Release an easy handle, allowing it to be reused or closed.
+ *
+ * This function must call unset on the easy handle's "handle" property.
+ *
+ * @param EasyHandle $easy
+ */
+ public function release(EasyHandle $easy);
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php
new file mode 100644
index 00000000..43577da6
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php
@@ -0,0 +1,45 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * HTTP handler that uses cURL easy handles as a transport layer.
+ *
+ * When using the CurlHandler, custom curl options can be specified as an
+ * associative array of curl option constants mapping to values in the
+ * **curl** key of the "client" key of the request.
+ */
+class CurlHandler
+{
+ /** @var CurlFactoryInterface */
+ private $factory;
+
+ /**
+ * Accepts an associative array of options:
+ *
+ * - factory: Optional curl factory used to create cURL handles.
+ *
+ * @param array $options Array of options to use with the handler
+ */
+ public function __construct(array $options = [])
+ {
+ $this->factory = isset($options['handle_factory'])
+ ? $options['handle_factory']
+ : new CurlFactory(3);
+ }
+
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ if (isset($options['delay'])) {
+ usleep($options['delay'] * 1000);
+ }
+
+ $easy = $this->factory->create($request, $options);
+ curl_exec($easy->handle);
+ $easy->errno = curl_errno($easy->handle);
+
+ return CurlFactory::finish($this, $easy, $this->factory);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
new file mode 100644
index 00000000..2754d8e4
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php
@@ -0,0 +1,199 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Promise as P;
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Returns an asynchronous response using curl_multi_* functions.
+ *
+ * When using the CurlMultiHandler, custom curl options can be specified as an
+ * associative array of curl option constants mapping to values in the
+ * **curl** key of the provided request options.
+ *
+ * @property resource $_mh Internal use only. Lazy loaded multi-handle.
+ */
+class CurlMultiHandler
+{
+ /** @var CurlFactoryInterface */
+ private $factory;
+ private $selectTimeout;
+ private $active;
+ private $handles = [];
+ private $delays = [];
+
+ /**
+ * This handler accepts the following options:
+ *
+ * - handle_factory: An optional factory used to create curl handles
+ * - select_timeout: Optional timeout (in seconds) to block before timing
+ * out while selecting curl handles. Defaults to 1 second.
+ *
+ * @param array $options
+ */
+ public function __construct(array $options = [])
+ {
+ $this->factory = isset($options['handle_factory'])
+ ? $options['handle_factory'] : new CurlFactory(50);
+ $this->selectTimeout = isset($options['select_timeout'])
+ ? $options['select_timeout'] : 1;
+ }
+
+ public function __get($name)
+ {
+ if ($name === '_mh') {
+ return $this->_mh = curl_multi_init();
+ }
+
+ throw new \BadMethodCallException();
+ }
+
+ public function __destruct()
+ {
+ if (isset($this->_mh)) {
+ curl_multi_close($this->_mh);
+ unset($this->_mh);
+ }
+ }
+
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ $easy = $this->factory->create($request, $options);
+ $id = (int) $easy->handle;
+
+ $promise = new Promise(
+ [$this, 'execute'],
+ function () use ($id) {
+ return $this->cancel($id);
+ }
+ );
+
+ $this->addRequest(['easy' => $easy, 'deferred' => $promise]);
+
+ return $promise;
+ }
+
+ /**
+ * Ticks the curl event loop.
+ */
+ public function tick()
+ {
+ // Add any delayed handles if needed.
+ if ($this->delays) {
+ $currentTime = microtime(true);
+ foreach ($this->delays as $id => $delay) {
+ if ($currentTime >= $delay) {
+ unset($this->delays[$id]);
+ curl_multi_add_handle(
+ $this->_mh,
+ $this->handles[$id]['easy']->handle
+ );
+ }
+ }
+ }
+
+ // Step through the task queue which may add additional requests.
+ P\queue()->run();
+
+ if ($this->active &&
+ curl_multi_select($this->_mh, $this->selectTimeout) === -1
+ ) {
+ // Perform a usleep if a select returns -1.
+ // See: https://bugs.php.net/bug.php?id=61141
+ usleep(250);
+ }
+
+ while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM);
+
+ $this->processMessages();
+ }
+
+ /**
+ * Runs until all outstanding connections have completed.
+ */
+ public function execute()
+ {
+ $queue = P\queue();
+
+ while ($this->handles || !$queue->isEmpty()) {
+ // If there are no transfers, then sleep for the next delay
+ if (!$this->active && $this->delays) {
+ usleep($this->timeToNext());
+ }
+ $this->tick();
+ }
+ }
+
+ private function addRequest(array $entry)
+ {
+ $easy = $entry['easy'];
+ $id = (int) $easy->handle;
+ $this->handles[$id] = $entry;
+ if (empty($easy->options['delay'])) {
+ curl_multi_add_handle($this->_mh, $easy->handle);
+ } else {
+ $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000);
+ }
+ }
+
+ /**
+ * Cancels a handle from sending and removes references to it.
+ *
+ * @param int $id Handle ID to cancel and remove.
+ *
+ * @return bool True on success, false on failure.
+ */
+ private function cancel($id)
+ {
+ // Cannot cancel if it has been processed.
+ if (!isset($this->handles[$id])) {
+ return false;
+ }
+
+ $handle = $this->handles[$id]['easy']->handle;
+ unset($this->delays[$id], $this->handles[$id]);
+ curl_multi_remove_handle($this->_mh, $handle);
+ curl_close($handle);
+
+ return true;
+ }
+
+ private function processMessages()
+ {
+ while ($done = curl_multi_info_read($this->_mh)) {
+ $id = (int) $done['handle'];
+ curl_multi_remove_handle($this->_mh, $done['handle']);
+
+ if (!isset($this->handles[$id])) {
+ // Probably was cancelled.
+ continue;
+ }
+
+ $entry = $this->handles[$id];
+ unset($this->handles[$id], $this->delays[$id]);
+ $entry['easy']->errno = $done['result'];
+ $entry['deferred']->resolve(
+ CurlFactory::finish(
+ $this,
+ $entry['easy'],
+ $this->factory
+ )
+ );
+ }
+ }
+
+ private function timeToNext()
+ {
+ $currentTime = microtime(true);
+ $nextTime = PHP_INT_MAX;
+ foreach ($this->delays as $time) {
+ if ($time < $nextTime) {
+ $nextTime = $time;
+ }
+ }
+
+ return max(0, $nextTime - $currentTime) * 1000000;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
new file mode 100644
index 00000000..7754e911
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php
@@ -0,0 +1,92 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Psr7\Response;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Represents a cURL easy handle and the data it populates.
+ *
+ * @internal
+ */
+final class EasyHandle
+{
+ /** @var resource cURL resource */
+ public $handle;
+
+ /** @var StreamInterface Where data is being written */
+ public $sink;
+
+ /** @var array Received HTTP headers so far */
+ public $headers = [];
+
+ /** @var ResponseInterface Received response (if any) */
+ public $response;
+
+ /** @var RequestInterface Request being sent */
+ public $request;
+
+ /** @var array Request options */
+ public $options = [];
+
+ /** @var int cURL error number (if any) */
+ public $errno = 0;
+
+ /** @var \Exception Exception during on_headers (if any) */
+ public $onHeadersException;
+
+ /**
+ * Attach a response to the easy handle based on the received headers.
+ *
+ * @throws \RuntimeException if no headers have been received.
+ */
+ public function createResponse()
+ {
+ if (empty($this->headers)) {
+ throw new \RuntimeException('No headers have been received');
+ }
+
+ // HTTP-version SP status-code SP reason-phrase
+ $startLine = explode(' ', array_shift($this->headers), 3);
+ $headers = \GuzzleHttp\headers_from_lines($this->headers);
+ $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
+
+ if (!empty($this->options['decode_content'])
+ && isset($normalizedKeys['content-encoding'])
+ ) {
+ $headers['x-encoded-content-encoding']
+ = $headers[$normalizedKeys['content-encoding']];
+ unset($headers[$normalizedKeys['content-encoding']]);
+ if (isset($normalizedKeys['content-length'])) {
+ $headers['x-encoded-content-length']
+ = $headers[$normalizedKeys['content-length']];
+
+ $bodyLength = (int) $this->sink->getSize();
+ if ($bodyLength) {
+ $headers[$normalizedKeys['content-length']] = $bodyLength;
+ } else {
+ unset($headers[$normalizedKeys['content-length']]);
+ }
+ }
+ }
+
+ // Attach a response to the easy handle with the parsed headers.
+ $this->response = new Response(
+ $startLine[1],
+ $headers,
+ $this->sink,
+ substr($startLine[0], 5),
+ isset($startLine[2]) ? (string) $startLine[2] : null
+ );
+ }
+
+ public function __get($name)
+ {
+ $msg = $name === 'handle'
+ ? 'The EasyHandle has been released'
+ : 'Invalid property: ' . $name;
+ throw new \BadMethodCallException($msg);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
new file mode 100644
index 00000000..d892061c
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php
@@ -0,0 +1,189 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\HandlerStack;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\TransferStats;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Handler that returns responses or throw exceptions from a queue.
+ */
+class MockHandler implements \Countable
+{
+ private $queue = [];
+ private $lastRequest;
+ private $lastOptions;
+ private $onFulfilled;
+ private $onRejected;
+
+ /**
+ * Creates a new MockHandler that uses the default handler stack list of
+ * middlewares.
+ *
+ * @param array $queue Array of responses, callables, or exceptions.
+ * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
+ * @param callable $onRejected Callback to invoke when the return value is rejected.
+ *
+ * @return HandlerStack
+ */
+ public static function createWithMiddleware(
+ array $queue = null,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
+ }
+
+ /**
+ * The passed in value must be an array of
+ * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
+ * callables, or Promises.
+ *
+ * @param array $queue
+ * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
+ * @param callable $onRejected Callback to invoke when the return value is rejected.
+ */
+ public function __construct(
+ array $queue = null,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ $this->onFulfilled = $onFulfilled;
+ $this->onRejected = $onRejected;
+
+ if ($queue) {
+ call_user_func_array([$this, 'append'], $queue);
+ }
+ }
+
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ if (!$this->queue) {
+ throw new \OutOfBoundsException('Mock queue is empty');
+ }
+
+ if (isset($options['delay'])) {
+ usleep($options['delay'] * 1000);
+ }
+
+ $this->lastRequest = $request;
+ $this->lastOptions = $options;
+ $response = array_shift($this->queue);
+
+ if (isset($options['on_headers'])) {
+ if (!is_callable($options['on_headers'])) {
+ throw new \InvalidArgumentException('on_headers must be callable');
+ }
+ try {
+ $options['on_headers']($response);
+ } catch (\Exception $e) {
+ $msg = 'An error was encountered during the on_headers event';
+ $response = new RequestException($msg, $request, $response, $e);
+ }
+ }
+
+ if (is_callable($response)) {
+ $response = call_user_func($response, $request, $options);
+ }
+
+ $response = $response instanceof \Exception
+ ? \GuzzleHttp\Promise\rejection_for($response)
+ : \GuzzleHttp\Promise\promise_for($response);
+
+ return $response->then(
+ function ($value) use ($request, $options) {
+ $this->invokeStats($request, $options, $value);
+ if ($this->onFulfilled) {
+ call_user_func($this->onFulfilled, $value);
+ }
+ if (isset($options['sink'])) {
+ $contents = (string) $value->getBody();
+ $sink = $options['sink'];
+
+ if (is_resource($sink)) {
+ fwrite($sink, $contents);
+ } elseif (is_string($sink)) {
+ file_put_contents($sink, $contents);
+ } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
+ $sink->write($contents);
+ }
+ }
+
+ return $value;
+ },
+ function ($reason) use ($request, $options) {
+ $this->invokeStats($request, $options, null, $reason);
+ if ($this->onRejected) {
+ call_user_func($this->onRejected, $reason);
+ }
+ return \GuzzleHttp\Promise\rejection_for($reason);
+ }
+ );
+ }
+
+ /**
+ * Adds one or more variadic requests, exceptions, callables, or promises
+ * to the queue.
+ */
+ public function append()
+ {
+ foreach (func_get_args() as $value) {
+ if ($value instanceof ResponseInterface
+ || $value instanceof \Exception
+ || $value instanceof PromiseInterface
+ || is_callable($value)
+ ) {
+ $this->queue[] = $value;
+ } else {
+ throw new \InvalidArgumentException('Expected a response or '
+ . 'exception. Found ' . \GuzzleHttp\describe_type($value));
+ }
+ }
+ }
+
+ /**
+ * Get the last received request.
+ *
+ * @return RequestInterface
+ */
+ public function getLastRequest()
+ {
+ return $this->lastRequest;
+ }
+
+ /**
+ * Get the last received request options.
+ *
+ * @return array
+ */
+ public function getLastOptions()
+ {
+ return $this->lastOptions;
+ }
+
+ /**
+ * Returns the number of remaining items in the queue.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return count($this->queue);
+ }
+
+ private function invokeStats(
+ RequestInterface $request,
+ array $options,
+ ResponseInterface $response = null,
+ $reason = null
+ ) {
+ if (isset($options['on_stats'])) {
+ $stats = new TransferStats($request, $response, 0, $reason);
+ call_user_func($options['on_stats'], $stats);
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
new file mode 100644
index 00000000..f8b00be0
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php
@@ -0,0 +1,55 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\RequestOptions;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Provides basic proxies for handlers.
+ */
+class Proxy
+{
+ /**
+ * Sends synchronous requests to a specific handler while sending all other
+ * requests to another handler.
+ *
+ * @param callable $default Handler used for normal responses
+ * @param callable $sync Handler used for synchronous responses.
+ *
+ * @return callable Returns the composed handler.
+ */
+ public static function wrapSync(
+ callable $default,
+ callable $sync
+ ) {
+ return function (RequestInterface $request, array $options) use ($default, $sync) {
+ return empty($options[RequestOptions::SYNCHRONOUS])
+ ? $default($request, $options)
+ : $sync($request, $options);
+ };
+ }
+
+ /**
+ * Sends streaming requests to a streaming compatible handler while sending
+ * all other requests to a default handler.
+ *
+ * This, for example, could be useful for taking advantage of the
+ * performance benefits of curl while still supporting true streaming
+ * through the StreamHandler.
+ *
+ * @param callable $default Handler used for non-streaming responses
+ * @param callable $streaming Handler used for streaming responses
+ *
+ * @return callable Returns the composed handler.
+ */
+ public static function wrapStreaming(
+ callable $default,
+ callable $streaming
+ ) {
+ return function (RequestInterface $request, array $options) use ($default, $streaming) {
+ return empty($options['stream'])
+ ? $default($request, $options)
+ : $streaming($request, $options);
+ };
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
new file mode 100644
index 00000000..b686545e
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php
@@ -0,0 +1,532 @@
+<?php
+namespace GuzzleHttp\Handler;
+
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Exception\ConnectException;
+use GuzzleHttp\Promise\FulfilledPromise;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Psr7;
+use GuzzleHttp\TransferStats;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * HTTP handler that uses PHP's HTTP stream wrapper.
+ */
+class StreamHandler
+{
+ private $lastHeaders = [];
+
+ /**
+ * Sends an HTTP request.
+ *
+ * @param RequestInterface $request Request to send.
+ * @param array $options Request transfer options.
+ *
+ * @return PromiseInterface
+ */
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ // Sleep if there is a delay specified.
+ if (isset($options['delay'])) {
+ usleep($options['delay'] * 1000);
+ }
+
+ $startTime = isset($options['on_stats']) ? microtime(true) : null;
+
+ try {
+ // Does not support the expect header.
+ $request = $request->withoutHeader('Expect');
+
+ // Append a content-length header if body size is zero to match
+ // cURL's behavior.
+ if (0 === $request->getBody()->getSize()) {
+ $request = $request->withHeader('Content-Length', 0);
+ }
+
+ return $this->createResponse(
+ $request,
+ $options,
+ $this->createStream($request, $options),
+ $startTime
+ );
+ } catch (\InvalidArgumentException $e) {
+ throw $e;
+ } catch (\Exception $e) {
+ // Determine if the error was a networking error.
+ $message = $e->getMessage();
+ // This list can probably get more comprehensive.
+ if (strpos($message, 'getaddrinfo') // DNS lookup failed
+ || strpos($message, 'Connection refused')
+ || strpos($message, "couldn't connect to host") // error on HHVM
+ || strpos($message, "connection attempt failed")
+ ) {
+ $e = new ConnectException($e->getMessage(), $request, $e);
+ }
+ $e = RequestException::wrapException($request, $e);
+ $this->invokeStats($options, $request, $startTime, null, $e);
+
+ return \GuzzleHttp\Promise\rejection_for($e);
+ }
+ }
+
+ private function invokeStats(
+ array $options,
+ RequestInterface $request,
+ $startTime,
+ ResponseInterface $response = null,
+ $error = null
+ ) {
+ if (isset($options['on_stats'])) {
+ $stats = new TransferStats(
+ $request,
+ $response,
+ microtime(true) - $startTime,
+ $error,
+ []
+ );
+ call_user_func($options['on_stats'], $stats);
+ }
+ }
+
+ private function createResponse(
+ RequestInterface $request,
+ array $options,
+ $stream,
+ $startTime
+ ) {
+ $hdrs = $this->lastHeaders;
+ $this->lastHeaders = [];
+ $parts = explode(' ', array_shift($hdrs), 3);
+ $ver = explode('/', $parts[0])[1];
+ $status = $parts[1];
+ $reason = isset($parts[2]) ? $parts[2] : null;
+ $headers = \GuzzleHttp\headers_from_lines($hdrs);
+ list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
+ $stream = Psr7\stream_for($stream);
+ $sink = $stream;
+
+ if (strcasecmp('HEAD', $request->getMethod())) {
+ $sink = $this->createSink($stream, $options);
+ }
+
+ $response = new Psr7\Response($status, $headers, $sink, $ver, $reason);
+
+ if (isset($options['on_headers'])) {
+ try {
+ $options['on_headers']($response);
+ } catch (\Exception $e) {
+ $msg = 'An error was encountered during the on_headers event';
+ $ex = new RequestException($msg, $request, $response, $e);
+ return \GuzzleHttp\Promise\rejection_for($ex);
+ }
+ }
+
+ // Do not drain when the request is a HEAD request because they have
+ // no body.
+ if ($sink !== $stream) {
+ $this->drain(
+ $stream,
+ $sink,
+ $response->getHeaderLine('Content-Length')
+ );
+ }
+
+ $this->invokeStats($options, $request, $startTime, $response, null);
+
+ return new FulfilledPromise($response);
+ }
+
+ private function createSink(StreamInterface $stream, array $options)
+ {
+ if (!empty($options['stream'])) {
+ return $stream;
+ }
+
+ $sink = isset($options['sink'])
+ ? $options['sink']
+ : fopen('php://temp', 'r+');
+
+ return is_string($sink)
+ ? new Psr7\LazyOpenStream($sink, 'w+')
+ : Psr7\stream_for($sink);
+ }
+
+ private function checkDecode(array $options, array $headers, $stream)
+ {
+ // Automatically decode responses when instructed.
+ if (!empty($options['decode_content'])) {
+ $normalizedKeys = \GuzzleHttp\normalize_header_keys($headers);
+ if (isset($normalizedKeys['content-encoding'])) {
+ $encoding = $headers[$normalizedKeys['content-encoding']];
+ if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
+ $stream = new Psr7\InflateStream(
+ Psr7\stream_for($stream)
+ );
+ $headers['x-encoded-content-encoding']
+ = $headers[$normalizedKeys['content-encoding']];
+ // Remove content-encoding header
+ unset($headers[$normalizedKeys['content-encoding']]);
+ // Fix content-length header
+ if (isset($normalizedKeys['content-length'])) {
+ $headers['x-encoded-content-length']
+ = $headers[$normalizedKeys['content-length']];
+
+ $length = (int) $stream->getSize();
+ if ($length === 0) {
+ unset($headers[$normalizedKeys['content-length']]);
+ } else {
+ $headers[$normalizedKeys['content-length']] = [$length];
+ }
+ }
+ }
+ }
+ }
+
+ return [$stream, $headers];
+ }
+
+ /**
+ * Drains the source stream into the "sink" client option.
+ *
+ * @param StreamInterface $source
+ * @param StreamInterface $sink
+ * @param string $contentLength Header specifying the amount of
+ * data to read.
+ *
+ * @return StreamInterface
+ * @throws \RuntimeException when the sink option is invalid.
+ */
+ private function drain(
+ StreamInterface $source,
+ StreamInterface $sink,
+ $contentLength
+ ) {
+ // If a content-length header is provided, then stop reading once
+ // that number of bytes has been read. This can prevent infinitely
+ // reading from a stream when dealing with servers that do not honor
+ // Connection: Close headers.
+ Psr7\copy_to_stream(
+ $source,
+ $sink,
+ (strlen($contentLength) > 0 && (int) $contentLength > 0) ? (int) $contentLength : -1
+ );
+
+ $sink->seek(0);
+ $source->close();
+
+ return $sink;
+ }
+
+ /**
+ * Create a resource and check to ensure it was created successfully
+ *
+ * @param callable $callback Callable that returns stream resource
+ *
+ * @return resource
+ * @throws \RuntimeException on error
+ */
+ private function createResource(callable $callback)
+ {
+ $errors = null;
+ set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
+ $errors[] = [
+ 'message' => $msg,
+ 'file' => $file,
+ 'line' => $line
+ ];
+ return true;
+ });
+
+ $resource = $callback();
+ restore_error_handler();
+
+ if (!$resource) {
+ $message = 'Error creating resource: ';
+ foreach ($errors as $err) {
+ foreach ($err as $key => $value) {
+ $message .= "[$key] $value" . PHP_EOL;
+ }
+ }
+ throw new \RuntimeException(trim($message));
+ }
+
+ return $resource;
+ }
+
+ private function createStream(RequestInterface $request, array $options)
+ {
+ static $methods;
+ if (!$methods) {
+ $methods = array_flip(get_class_methods(__CLASS__));
+ }
+
+ // HTTP/1.1 streams using the PHP stream wrapper require a
+ // Connection: close header
+ if ($request->getProtocolVersion() == '1.1'
+ && !$request->hasHeader('Connection')
+ ) {
+ $request = $request->withHeader('Connection', 'close');
+ }
+
+ // Ensure SSL is verified by default
+ if (!isset($options['verify'])) {
+ $options['verify'] = true;
+ }
+
+ $params = [];
+ $context = $this->getDefaultContext($request);
+
+ if (isset($options['on_headers']) && !is_callable($options['on_headers'])) {
+ throw new \InvalidArgumentException('on_headers must be callable');
+ }
+
+ if (!empty($options)) {
+ foreach ($options as $key => $value) {
+ $method = "add_{$key}";
+ if (isset($methods[$method])) {
+ $this->{$method}($request, $context, $value, $params);
+ }
+ }
+ }
+
+ if (isset($options['stream_context'])) {
+ if (!is_array($options['stream_context'])) {
+ throw new \InvalidArgumentException('stream_context must be an array');
+ }
+ $context = array_replace_recursive(
+ $context,
+ $options['stream_context']
+ );
+ }
+
+ // Microsoft NTLM authentication only supported with curl handler
+ if (isset($options['auth'])
+ && is_array($options['auth'])
+ && isset($options['auth'][2])
+ && 'ntlm' == $options['auth'][2]
+ ) {
+ throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
+ }
+
+ $uri = $this->resolveHost($request, $options);
+
+ $context = $this->createResource(
+ function () use ($context, $params) {
+ return stream_context_create($context, $params);
+ }
+ );
+
+ return $this->createResource(
+ function () use ($uri, &$http_response_header, $context, $options) {
+ $resource = fopen((string) $uri, 'r', null, $context);
+ $this->lastHeaders = $http_response_header;
+
+ if (isset($options['read_timeout'])) {
+ $readTimeout = $options['read_timeout'];
+ $sec = (int) $readTimeout;
+ $usec = ($readTimeout - $sec) * 100000;
+ stream_set_timeout($resource, $sec, $usec);
+ }
+
+ return $resource;
+ }
+ );
+ }
+
+ private function resolveHost(RequestInterface $request, array $options)
+ {
+ $uri = $request->getUri();
+
+ if (isset($options['force_ip_resolve']) && !filter_var($uri->getHost(), FILTER_VALIDATE_IP)) {
+ if ('v4' === $options['force_ip_resolve']) {
+ $records = dns_get_record($uri->getHost(), DNS_A);
+ if (!isset($records[0]['ip'])) {
+ throw new ConnectException(sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
+ }
+ $uri = $uri->withHost($records[0]['ip']);
+ } elseif ('v6' === $options['force_ip_resolve']) {
+ $records = dns_get_record($uri->getHost(), DNS_AAAA);
+ if (!isset($records[0]['ipv6'])) {
+ throw new ConnectException(sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
+ }
+ $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
+ }
+ }
+
+ return $uri;
+ }
+
+ private function getDefaultContext(RequestInterface $request)
+ {
+ $headers = '';
+ foreach ($request->getHeaders() as $name => $value) {
+ foreach ($value as $val) {
+ $headers .= "$name: $val\r\n";
+ }
+ }
+
+ $context = [
+ 'http' => [
+ 'method' => $request->getMethod(),
+ 'header' => $headers,
+ 'protocol_version' => $request->getProtocolVersion(),
+ 'ignore_errors' => true,
+ 'follow_location' => 0,
+ ],
+ ];
+
+ $body = (string) $request->getBody();
+
+ if (!empty($body)) {
+ $context['http']['content'] = $body;
+ // Prevent the HTTP handler from adding a Content-Type header.
+ if (!$request->hasHeader('Content-Type')) {
+ $context['http']['header'] .= "Content-Type:\r\n";
+ }
+ }
+
+ $context['http']['header'] = rtrim($context['http']['header']);
+
+ return $context;
+ }
+
+ private function add_proxy(RequestInterface $request, &$options, $value, &$params)
+ {
+ if (!is_array($value)) {
+ $options['http']['proxy'] = $value;
+ } else {
+ $scheme = $request->getUri()->getScheme();
+ if (isset($value[$scheme])) {
+ if (!isset($value['no'])
+ || !\GuzzleHttp\is_host_in_noproxy(
+ $request->getUri()->getHost(),
+ $value['no']
+ )
+ ) {
+ $options['http']['proxy'] = $value[$scheme];
+ }
+ }
+ }
+ }
+
+ private function add_timeout(RequestInterface $request, &$options, $value, &$params)
+ {
+ if ($value > 0) {
+ $options['http']['timeout'] = $value;
+ }
+ }
+
+ private function add_verify(RequestInterface $request, &$options, $value, &$params)
+ {
+ if ($value === true) {
+ // PHP 5.6 or greater will find the system cert by default. When
+ // < 5.6, use the Guzzle bundled cacert.
+ if (PHP_VERSION_ID < 50600) {
+ $options['ssl']['cafile'] = \GuzzleHttp\default_ca_bundle();
+ }
+ } elseif (is_string($value)) {
+ $options['ssl']['cafile'] = $value;
+ if (!file_exists($value)) {
+ throw new \RuntimeException("SSL CA bundle not found: $value");
+ }
+ } elseif ($value === false) {
+ $options['ssl']['verify_peer'] = false;
+ $options['ssl']['verify_peer_name'] = false;
+ return;
+ } else {
+ throw new \InvalidArgumentException('Invalid verify request option');
+ }
+
+ $options['ssl']['verify_peer'] = true;
+ $options['ssl']['verify_peer_name'] = true;
+ $options['ssl']['allow_self_signed'] = false;
+ }
+
+ private function add_cert(RequestInterface $request, &$options, $value, &$params)
+ {
+ if (is_array($value)) {
+ $options['ssl']['passphrase'] = $value[1];
+ $value = $value[0];
+ }
+
+ if (!file_exists($value)) {
+ throw new \RuntimeException("SSL certificate not found: {$value}");
+ }
+
+ $options['ssl']['local_cert'] = $value;
+ }
+
+ private function add_progress(RequestInterface $request, &$options, $value, &$params)
+ {
+ $this->addNotification(
+ $params,
+ function ($code, $a, $b, $c, $transferred, $total) use ($value) {
+ if ($code == STREAM_NOTIFY_PROGRESS) {
+ $value($total, $transferred, null, null);
+ }
+ }
+ );
+ }
+
+ private function add_debug(RequestInterface $request, &$options, $value, &$params)
+ {
+ if ($value === false) {
+ return;
+ }
+
+ static $map = [
+ STREAM_NOTIFY_CONNECT => 'CONNECT',
+ STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
+ STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
+ STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
+ STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
+ STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
+ STREAM_NOTIFY_PROGRESS => 'PROGRESS',
+ STREAM_NOTIFY_FAILURE => 'FAILURE',
+ STREAM_NOTIFY_COMPLETED => 'COMPLETED',
+ STREAM_NOTIFY_RESOLVE => 'RESOLVE',
+ ];
+ static $args = ['severity', 'message', 'message_code',
+ 'bytes_transferred', 'bytes_max'];
+
+ $value = \GuzzleHttp\debug_resource($value);
+ $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
+ $this->addNotification(
+ $params,
+ function () use ($ident, $value, $map, $args) {
+ $passed = func_get_args();
+ $code = array_shift($passed);
+ fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
+ foreach (array_filter($passed) as $i => $v) {
+ fwrite($value, $args[$i] . ': "' . $v . '" ');
+ }
+ fwrite($value, "\n");
+ }
+ );
+ }
+
+ private function addNotification(array &$params, callable $notify)
+ {
+ // Wrap the existing function if needed.
+ if (!isset($params['notification'])) {
+ $params['notification'] = $notify;
+ } else {
+ $params['notification'] = $this->callArray([
+ $params['notification'],
+ $notify
+ ]);
+ }
+ }
+
+ private function callArray(array $functions)
+ {
+ return function () use ($functions) {
+ $args = func_get_args();
+ foreach ($functions as $fn) {
+ call_user_func_array($fn, $args);
+ }
+ };
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/HandlerStack.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/HandlerStack.php
new file mode 100644
index 00000000..24c46fd9
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/HandlerStack.php
@@ -0,0 +1,273 @@
+<?php
+namespace GuzzleHttp;
+
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Creates a composed Guzzle handler function by stacking middlewares on top of
+ * an HTTP handler function.
+ */
+class HandlerStack
+{
+ /** @var callable */
+ private $handler;
+
+ /** @var array */
+ private $stack = [];
+
+ /** @var callable|null */
+ private $cached;
+
+ /**
+ * Creates a default handler stack that can be used by clients.
+ *
+ * The returned handler will wrap the provided handler or use the most
+ * appropriate default handler for your system. The returned HandlerStack has
+ * support for cookies, redirects, HTTP error exceptions, and preparing a body
+ * before sending.
+ *
+ * The returned handler stack can be passed to a client in the "handler"
+ * option.
+ *
+ * @param callable $handler HTTP handler function to use with the stack. If no
+ * handler is provided, the best handler for your
+ * system will be utilized.
+ *
+ * @return HandlerStack
+ */
+ public static function create(callable $handler = null)
+ {
+ $stack = new self($handler ?: choose_handler());
+ $stack->push(Middleware::httpErrors(), 'http_errors');
+ $stack->push(Middleware::redirect(), 'allow_redirects');
+ $stack->push(Middleware::cookies(), 'cookies');
+ $stack->push(Middleware::prepareBody(), 'prepare_body');
+
+ return $stack;
+ }
+
+ /**
+ * @param callable $handler Underlying HTTP handler.
+ */
+ public function __construct(callable $handler = null)
+ {
+ $this->handler = $handler;
+ }
+
+ /**
+ * Invokes the handler stack as a composed handler
+ *
+ * @param RequestInterface $request
+ * @param array $options
+ */
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ $handler = $this->resolve();
+
+ return $handler($request, $options);
+ }
+
+ /**
+ * Dumps a string representation of the stack.
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ $depth = 0;
+ $stack = [];
+ if ($this->handler) {
+ $stack[] = "0) Handler: " . $this->debugCallable($this->handler);
+ }
+
+ $result = '';
+ foreach (array_reverse($this->stack) as $tuple) {
+ $depth++;
+ $str = "{$depth}) Name: '{$tuple[1]}', ";
+ $str .= "Function: " . $this->debugCallable($tuple[0]);
+ $result = "> {$str}\n{$result}";
+ $stack[] = $str;
+ }
+
+ foreach (array_keys($stack) as $k) {
+ $result .= "< {$stack[$k]}\n";
+ }
+
+ return $result;
+ }
+
+ /**
+ * Set the HTTP handler that actually returns a promise.
+ *
+ * @param callable $handler Accepts a request and array of options and
+ * returns a Promise.
+ */
+ public function setHandler(callable $handler)
+ {
+ $this->handler = $handler;
+ $this->cached = null;
+ }
+
+ /**
+ * Returns true if the builder has a handler.
+ *
+ * @return bool
+ */
+ public function hasHandler()
+ {
+ return (bool) $this->handler;
+ }
+
+ /**
+ * Unshift a middleware to the bottom of the stack.
+ *
+ * @param callable $middleware Middleware function
+ * @param string $name Name to register for this middleware.
+ */
+ public function unshift(callable $middleware, $name = null)
+ {
+ array_unshift($this->stack, [$middleware, $name]);
+ $this->cached = null;
+ }
+
+ /**
+ * Push a middleware to the top of the stack.
+ *
+ * @param callable $middleware Middleware function
+ * @param string $name Name to register for this middleware.
+ */
+ public function push(callable $middleware, $name = '')
+ {
+ $this->stack[] = [$middleware, $name];
+ $this->cached = null;
+ }
+
+ /**
+ * Add a middleware before another middleware by name.
+ *
+ * @param string $findName Middleware to find
+ * @param callable $middleware Middleware function
+ * @param string $withName Name to register for this middleware.
+ */
+ public function before($findName, callable $middleware, $withName = '')
+ {
+ $this->splice($findName, $withName, $middleware, true);
+ }
+
+ /**
+ * Add a middleware after another middleware by name.
+ *
+ * @param string $findName Middleware to find
+ * @param callable $middleware Middleware function
+ * @param string $withName Name to register for this middleware.
+ */
+ public function after($findName, callable $middleware, $withName = '')
+ {
+ $this->splice($findName, $withName, $middleware, false);
+ }
+
+ /**
+ * Remove a middleware by instance or name from the stack.
+ *
+ * @param callable|string $remove Middleware to remove by instance or name.
+ */
+ public function remove($remove)
+ {
+ $this->cached = null;
+ $idx = is_callable($remove) ? 0 : 1;
+ $this->stack = array_values(array_filter(
+ $this->stack,
+ function ($tuple) use ($idx, $remove) {
+ return $tuple[$idx] !== $remove;
+ }
+ ));
+ }
+
+ /**
+ * Compose the middleware and handler into a single callable function.
+ *
+ * @return callable
+ */
+ public function resolve()
+ {
+ if (!$this->cached) {
+ if (!($prev = $this->handler)) {
+ throw new \LogicException('No handler has been specified');
+ }
+
+ foreach (array_reverse($this->stack) as $fn) {
+ $prev = $fn[0]($prev);
+ }
+
+ $this->cached = $prev;
+ }
+
+ return $this->cached;
+ }
+
+ /**
+ * @param $name
+ * @return int
+ */
+ private function findByName($name)
+ {
+ foreach ($this->stack as $k => $v) {
+ if ($v[1] === $name) {
+ return $k;
+ }
+ }
+
+ throw new \InvalidArgumentException("Middleware not found: $name");
+ }
+
+ /**
+ * Splices a function into the middleware list at a specific position.
+ *
+ * @param $findName
+ * @param $withName
+ * @param callable $middleware
+ * @param $before
+ */
+ private function splice($findName, $withName, callable $middleware, $before)
+ {
+ $this->cached = null;
+ $idx = $this->findByName($findName);
+ $tuple = [$middleware, $withName];
+
+ if ($before) {
+ if ($idx === 0) {
+ array_unshift($this->stack, $tuple);
+ } else {
+ $replacement = [$tuple, $this->stack[$idx]];
+ array_splice($this->stack, $idx, 1, $replacement);
+ }
+ } elseif ($idx === count($this->stack) - 1) {
+ $this->stack[] = $tuple;
+ } else {
+ $replacement = [$this->stack[$idx], $tuple];
+ array_splice($this->stack, $idx, 1, $replacement);
+ }
+ }
+
+ /**
+ * Provides a debug string for a given callable.
+ *
+ * @param array|callable $fn Function to write as a string.
+ *
+ * @return string
+ */
+ private function debugCallable($fn)
+ {
+ if (is_string($fn)) {
+ return "callable({$fn})";
+ }
+
+ if (is_array($fn)) {
+ return is_string($fn[0])
+ ? "callable({$fn[0]}::{$fn[1]})"
+ : "callable(['" . get_class($fn[0]) . "', '{$fn[1]}'])";
+ }
+
+ return 'callable(' . spl_object_hash($fn) . ')';
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
new file mode 100644
index 00000000..663ac739
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/MessageFormatter.php
@@ -0,0 +1,180 @@
+<?php
+namespace GuzzleHttp;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Formats log messages using variable substitutions for requests, responses,
+ * and other transactional data.
+ *
+ * The following variable substitutions are supported:
+ *
+ * - {request}: Full HTTP request message
+ * - {response}: Full HTTP response message
+ * - {ts}: ISO 8601 date in GMT
+ * - {date_iso_8601} ISO 8601 date in GMT
+ * - {date_common_log} Apache common log date using the configured timezone.
+ * - {host}: Host of the request
+ * - {method}: Method of the request
+ * - {uri}: URI of the request
+ * - {version}: Protocol version
+ * - {target}: Request target of the request (path + query + fragment)
+ * - {hostname}: Hostname of the machine that sent the request
+ * - {code}: Status code of the response (if available)
+ * - {phrase}: Reason phrase of the response (if available)
+ * - {error}: Any error messages (if available)
+ * - {req_header_*}: Replace `*` with the lowercased name of a request header to add to the message
+ * - {res_header_*}: Replace `*` with the lowercased name of a response header to add to the message
+ * - {req_headers}: Request headers
+ * - {res_headers}: Response headers
+ * - {req_body}: Request body
+ * - {res_body}: Response body
+ */
+class MessageFormatter
+{
+ /**
+ * Apache Common Log Format.
+ * @link http://httpd.apache.org/docs/2.4/logs.html#common
+ * @var string
+ */
+ const CLF = "{hostname} {req_header_User-Agent} - [{date_common_log}] \"{method} {target} HTTP/{version}\" {code} {res_header_Content-Length}";
+ const DEBUG = ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}";
+ const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}';
+
+ /** @var string Template used to format log messages */
+ private $template;
+
+ /**
+ * @param string $template Log message template
+ */
+ public function __construct($template = self::CLF)
+ {
+ $this->template = $template ?: self::CLF;
+ }
+
+ /**
+ * Returns a formatted message string.
+ *
+ * @param RequestInterface $request Request that was sent
+ * @param ResponseInterface $response Response that was received
+ * @param \Exception $error Exception that was received
+ *
+ * @return string
+ */
+ public function format(
+ RequestInterface $request,
+ ResponseInterface $response = null,
+ \Exception $error = null
+ ) {
+ $cache = [];
+
+ return preg_replace_callback(
+ '/{\s*([A-Za-z_\-\.0-9]+)\s*}/',
+ function (array $matches) use ($request, $response, $error, &$cache) {
+ if (isset($cache[$matches[1]])) {
+ return $cache[$matches[1]];
+ }
+
+ $result = '';
+ switch ($matches[1]) {
+ case 'request':
+ $result = Psr7\str($request);
+ break;
+ case 'response':
+ $result = $response ? Psr7\str($response) : '';
+ break;
+ case 'req_headers':
+ $result = trim($request->getMethod()
+ . ' ' . $request->getRequestTarget())
+ . ' HTTP/' . $request->getProtocolVersion() . "\r\n"
+ . $this->headers($request);
+ break;
+ case 'res_headers':
+ $result = $response ?
+ sprintf(
+ 'HTTP/%s %d %s',
+ $response->getProtocolVersion(),
+ $response->getStatusCode(),
+ $response->getReasonPhrase()
+ ) . "\r\n" . $this->headers($response)
+ : 'NULL';
+ break;
+ case 'req_body':
+ $result = $request->getBody();
+ break;
+ case 'res_body':
+ $result = $response ? $response->getBody() : 'NULL';
+ break;
+ case 'ts':
+ case 'date_iso_8601':
+ $result = gmdate('c');
+ break;
+ case 'date_common_log':
+ $result = date('d/M/Y:H:i:s O');
+ break;
+ case 'method':
+ $result = $request->getMethod();
+ break;
+ case 'version':
+ $result = $request->getProtocolVersion();
+ break;
+ case 'uri':
+ case 'url':
+ $result = $request->getUri();
+ break;
+ case 'target':
+ $result = $request->getRequestTarget();
+ break;
+ case 'req_version':
+ $result = $request->getProtocolVersion();
+ break;
+ case 'res_version':
+ $result = $response
+ ? $response->getProtocolVersion()
+ : 'NULL';
+ break;
+ case 'host':
+ $result = $request->getHeaderLine('Host');
+ break;
+ case 'hostname':
+ $result = gethostname();
+ break;
+ case 'code':
+ $result = $response ? $response->getStatusCode() : 'NULL';
+ break;
+ case 'phrase':
+ $result = $response ? $response->getReasonPhrase() : 'NULL';
+ break;
+ case 'error':
+ $result = $error ? $error->getMessage() : 'NULL';
+ break;
+ default:
+ // handle prefixed dynamic headers
+ if (strpos($matches[1], 'req_header_') === 0) {
+ $result = $request->getHeaderLine(substr($matches[1], 11));
+ } elseif (strpos($matches[1], 'res_header_') === 0) {
+ $result = $response
+ ? $response->getHeaderLine(substr($matches[1], 11))
+ : 'NULL';
+ }
+ }
+
+ $cache[$matches[1]] = $result;
+ return $result;
+ },
+ $this->template
+ );
+ }
+
+ private function headers(MessageInterface $message)
+ {
+ $result = '';
+ foreach ($message->getHeaders() as $name => $values) {
+ $result .= $name . ': ' . implode(', ', $values) . "\r\n";
+ }
+
+ return trim($result);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Middleware.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Middleware.php
new file mode 100644
index 00000000..d4ad75c9
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Middleware.php
@@ -0,0 +1,255 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Cookie\CookieJarInterface;
+use GuzzleHttp\Exception\RequestException;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+/**
+ * Functions used to create and wrap handlers with handler middleware.
+ */
+final class Middleware
+{
+ /**
+ * Middleware that adds cookies to requests.
+ *
+ * The options array must be set to a CookieJarInterface in order to use
+ * cookies. This is typically handled for you by a client.
+ *
+ * @return callable Returns a function that accepts the next handler.
+ */
+ public static function cookies()
+ {
+ return function (callable $handler) {
+ return function ($request, array $options) use ($handler) {
+ if (empty($options['cookies'])) {
+ return $handler($request, $options);
+ } elseif (!($options['cookies'] instanceof CookieJarInterface)) {
+ throw new \InvalidArgumentException('cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface');
+ }
+ $cookieJar = $options['cookies'];
+ $request = $cookieJar->withCookieHeader($request);
+ return $handler($request, $options)
+ ->then(
+ function ($response) use ($cookieJar, $request) {
+ $cookieJar->extractCookies($request, $response);
+ return $response;
+ }
+ );
+ };
+ };
+ }
+
+ /**
+ * Middleware that throws exceptions for 4xx or 5xx responses when the
+ * "http_error" request option is set to true.
+ *
+ * @return callable Returns a function that accepts the next handler.
+ */
+ public static function httpErrors()
+ {
+ return function (callable $handler) {
+ return function ($request, array $options) use ($handler) {
+ if (empty($options['http_errors'])) {
+ return $handler($request, $options);
+ }
+ return $handler($request, $options)->then(
+ function (ResponseInterface $response) use ($request, $handler) {
+ $code = $response->getStatusCode();
+ if ($code < 400) {
+ return $response;
+ }
+ throw RequestException::create($request, $response);
+ }
+ );
+ };
+ };
+ }
+
+ /**
+ * Middleware that pushes history data to an ArrayAccess container.
+ *
+ * @param array|\ArrayAccess $container Container to hold the history (by reference).
+ *
+ * @return callable Returns a function that accepts the next handler.
+ * @throws \InvalidArgumentException if container is not an array or ArrayAccess.
+ */
+ public static function history(&$container)
+ {
+ if (!is_array($container) && !$container instanceof \ArrayAccess) {
+ throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess');
+ }
+
+ return function (callable $handler) use (&$container) {
+ return function ($request, array $options) use ($handler, &$container) {
+ return $handler($request, $options)->then(
+ function ($value) use ($request, &$container, $options) {
+ $container[] = [
+ 'request' => $request,
+ 'response' => $value,
+ 'error' => null,
+ 'options' => $options
+ ];
+ return $value;
+ },
+ function ($reason) use ($request, &$container, $options) {
+ $container[] = [
+ 'request' => $request,
+ 'response' => null,
+ 'error' => $reason,
+ 'options' => $options
+ ];
+ return \GuzzleHttp\Promise\rejection_for($reason);
+ }
+ );
+ };
+ };
+ }
+
+ /**
+ * Middleware that invokes a callback before and after sending a request.
+ *
+ * The provided listener cannot modify or alter the response. It simply
+ * "taps" into the chain to be notified before returning the promise. The
+ * before listener accepts a request and options array, and the after
+ * listener accepts a request, options array, and response promise.
+ *
+ * @param callable $before Function to invoke before forwarding the request.
+ * @param callable $after Function invoked after forwarding.
+ *
+ * @return callable Returns a function that accepts the next handler.
+ */
+ public static function tap(callable $before = null, callable $after = null)
+ {
+ return function (callable $handler) use ($before, $after) {
+ return function ($request, array $options) use ($handler, $before, $after) {
+ if ($before) {
+ $before($request, $options);
+ }
+ $response = $handler($request, $options);
+ if ($after) {
+ $after($request, $options, $response);
+ }
+ return $response;
+ };
+ };
+ }
+
+ /**
+ * Middleware that handles request redirects.
+ *
+ * @return callable Returns a function that accepts the next handler.
+ */
+ public static function redirect()
+ {
+ return function (callable $handler) {
+ return new RedirectMiddleware($handler);
+ };
+ }
+
+ /**
+ * Middleware that retries requests based on the boolean result of
+ * invoking the provided "decider" function.
+ *
+ * If no delay function is provided, a simple implementation of exponential
+ * backoff will be utilized.
+ *
+ * @param callable $decider Function that accepts the number of retries,
+ * a request, [response], and [exception] and
+ * returns true if the request is to be retried.
+ * @param callable $delay Function that accepts the number of retries and
+ * returns the number of milliseconds to delay.
+ *
+ * @return callable Returns a function that accepts the next handler.
+ */
+ public static function retry(callable $decider, callable $delay = null)
+ {
+ return function (callable $handler) use ($decider, $delay) {
+ return new RetryMiddleware($decider, $handler, $delay);
+ };
+ }
+
+ /**
+ * Middleware that logs requests, responses, and errors using a message
+ * formatter.
+ *
+ * @param LoggerInterface $logger Logs messages.
+ * @param MessageFormatter $formatter Formatter used to create message strings.
+ * @param string $logLevel Level at which to log requests.
+ *
+ * @return callable Returns a function that accepts the next handler.
+ */
+ public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
+ {
+ return function (callable $handler) use ($logger, $formatter, $logLevel) {
+ return function ($request, array $options) use ($handler, $logger, $formatter, $logLevel) {
+ return $handler($request, $options)->then(
+ function ($response) use ($logger, $request, $formatter, $logLevel) {
+ $message = $formatter->format($request, $response);
+ $logger->log($logLevel, $message);
+ return $response;
+ },
+ function ($reason) use ($logger, $request, $formatter) {
+ $response = $reason instanceof RequestException
+ ? $reason->getResponse()
+ : null;
+ $message = $formatter->format($request, $response, $reason);
+ $logger->notice($message);
+ return \GuzzleHttp\Promise\rejection_for($reason);
+ }
+ );
+ };
+ };
+ }
+
+ /**
+ * This middleware adds a default content-type if possible, a default
+ * content-length or transfer-encoding header, and the expect header.
+ *
+ * @return callable
+ */
+ public static function prepareBody()
+ {
+ return function (callable $handler) {
+ return new PrepareBodyMiddleware($handler);
+ };
+ }
+
+ /**
+ * Middleware that applies a map function to the request before passing to
+ * the next handler.
+ *
+ * @param callable $fn Function that accepts a RequestInterface and returns
+ * a RequestInterface.
+ * @return callable
+ */
+ public static function mapRequest(callable $fn)
+ {
+ return function (callable $handler) use ($fn) {
+ return function ($request, array $options) use ($handler, $fn) {
+ return $handler($fn($request), $options);
+ };
+ };
+ }
+
+ /**
+ * Middleware that applies a map function to the resolved promise's
+ * response.
+ *
+ * @param callable $fn Function that accepts a ResponseInterface and
+ * returns a ResponseInterface.
+ * @return callable
+ */
+ public static function mapResponse(callable $fn)
+ {
+ return function (callable $handler) use ($fn) {
+ return function ($request, array $options) use ($handler, $fn) {
+ return $handler($request, $options)->then($fn);
+ };
+ };
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/Pool.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/Pool.php
new file mode 100644
index 00000000..8f1be33c
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/Pool.php
@@ -0,0 +1,123 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromisorInterface;
+use Psr\Http\Message\RequestInterface;
+use GuzzleHttp\Promise\EachPromise;
+
+/**
+ * Sends and iterator of requests concurrently using a capped pool size.
+ *
+ * The pool will read from an iterator until it is cancelled or until the
+ * iterator is consumed. When a request is yielded, the request is sent after
+ * applying the "request_options" request options (if provided in the ctor).
+ *
+ * When a function is yielded by the iterator, the function is provided the
+ * "request_options" array that should be merged on top of any existing
+ * options, and the function MUST then return a wait-able promise.
+ */
+class Pool implements PromisorInterface
+{
+ /** @var EachPromise */
+ private $each;
+
+ /**
+ * @param ClientInterface $client Client used to send the requests.
+ * @param array|\Iterator $requests Requests or functions that return
+ * requests to send concurrently.
+ * @param array $config Associative array of options
+ * - concurrency: (int) Maximum number of requests to send concurrently
+ * - options: Array of request options to apply to each request.
+ * - fulfilled: (callable) Function to invoke when a request completes.
+ * - rejected: (callable) Function to invoke when a request is rejected.
+ */
+ public function __construct(
+ ClientInterface $client,
+ $requests,
+ array $config = []
+ ) {
+ // Backwards compatibility.
+ if (isset($config['pool_size'])) {
+ $config['concurrency'] = $config['pool_size'];
+ } elseif (!isset($config['concurrency'])) {
+ $config['concurrency'] = 25;
+ }
+
+ if (isset($config['options'])) {
+ $opts = $config['options'];
+ unset($config['options']);
+ } else {
+ $opts = [];
+ }
+
+ $iterable = \GuzzleHttp\Promise\iter_for($requests);
+ $requests = function () use ($iterable, $client, $opts) {
+ foreach ($iterable as $key => $rfn) {
+ if ($rfn instanceof RequestInterface) {
+ yield $key => $client->sendAsync($rfn, $opts);
+ } elseif (is_callable($rfn)) {
+ yield $key => $rfn($opts);
+ } else {
+ throw new \InvalidArgumentException('Each value yielded by '
+ . 'the iterator must be a Psr7\Http\Message\RequestInterface '
+ . 'or a callable that returns a promise that fulfills '
+ . 'with a Psr7\Message\Http\ResponseInterface object.');
+ }
+ }
+ };
+
+ $this->each = new EachPromise($requests(), $config);
+ }
+
+ public function promise()
+ {
+ return $this->each->promise();
+ }
+
+ /**
+ * Sends multiple requests concurrently and returns an array of responses
+ * and exceptions that uses the same ordering as the provided requests.
+ *
+ * IMPORTANT: This method keeps every request and response in memory, and
+ * as such, is NOT recommended when sending a large number or an
+ * indeterminate number of requests concurrently.
+ *
+ * @param ClientInterface $client Client used to send the requests
+ * @param array|\Iterator $requests Requests to send concurrently.
+ * @param array $options Passes through the options available in
+ * {@see GuzzleHttp\Pool::__construct}
+ *
+ * @return array Returns an array containing the response or an exception
+ * in the same order that the requests were sent.
+ * @throws \InvalidArgumentException if the event format is incorrect.
+ */
+ public static function batch(
+ ClientInterface $client,
+ $requests,
+ array $options = []
+ ) {
+ $res = [];
+ self::cmpCallback($options, 'fulfilled', $res);
+ self::cmpCallback($options, 'rejected', $res);
+ $pool = new static($client, $requests, $options);
+ $pool->promise()->wait();
+ ksort($res);
+
+ return $res;
+ }
+
+ private static function cmpCallback(array &$options, $name, array &$results)
+ {
+ if (!isset($options[$name])) {
+ $options[$name] = function ($v, $k) use (&$results) {
+ $results[$k] = $v;
+ };
+ } else {
+ $currentFn = $options[$name];
+ $options[$name] = function ($v, $k) use (&$results, $currentFn) {
+ $currentFn($v, $k);
+ $results[$k] = $v;
+ };
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
new file mode 100644
index 00000000..2eb95f9b
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php
@@ -0,0 +1,106 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+
+/**
+ * Prepares requests that contain a body, adding the Content-Length,
+ * Content-Type, and Expect headers.
+ */
+class PrepareBodyMiddleware
+{
+ /** @var callable */
+ private $nextHandler;
+
+ /**
+ * @param callable $nextHandler Next handler to invoke.
+ */
+ public function __construct(callable $nextHandler)
+ {
+ $this->nextHandler = $nextHandler;
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return PromiseInterface
+ */
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ $fn = $this->nextHandler;
+
+ // Don't do anything if the request has no body.
+ if ($request->getBody()->getSize() === 0) {
+ return $fn($request, $options);
+ }
+
+ $modify = [];
+
+ // Add a default content-type if possible.
+ if (!$request->hasHeader('Content-Type')) {
+ if ($uri = $request->getBody()->getMetadata('uri')) {
+ if ($type = Psr7\mimetype_from_filename($uri)) {
+ $modify['set_headers']['Content-Type'] = $type;
+ }
+ }
+ }
+
+ // Add a default content-length or transfer-encoding header.
+ if (!$request->hasHeader('Content-Length')
+ && !$request->hasHeader('Transfer-Encoding')
+ ) {
+ $size = $request->getBody()->getSize();
+ if ($size !== null) {
+ $modify['set_headers']['Content-Length'] = $size;
+ } else {
+ $modify['set_headers']['Transfer-Encoding'] = 'chunked';
+ }
+ }
+
+ // Add the expect header if needed.
+ $this->addExpectHeader($request, $options, $modify);
+
+ return $fn(Psr7\modify_request($request, $modify), $options);
+ }
+
+ private function addExpectHeader(
+ RequestInterface $request,
+ array $options,
+ array &$modify
+ ) {
+ // Determine if the Expect header should be used
+ if ($request->hasHeader('Expect')) {
+ return;
+ }
+
+ $expect = isset($options['expect']) ? $options['expect'] : null;
+
+ // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0
+ if ($expect === false || $request->getProtocolVersion() < 1.1) {
+ return;
+ }
+
+ // The expect header is unconditionally enabled
+ if ($expect === true) {
+ $modify['set_headers']['Expect'] = '100-Continue';
+ return;
+ }
+
+ // By default, send the expect header when the payload is > 1mb
+ if ($expect === null) {
+ $expect = 1048576;
+ }
+
+ // Always add if the body cannot be rewound, the size cannot be
+ // determined, or the size is greater than the cutoff threshold
+ $body = $request->getBody();
+ $size = $body->getSize();
+
+ if ($size === null || $size >= (int) $expect || !$body->isSeekable()) {
+ $modify['set_headers']['Expect'] = '100-Continue';
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
new file mode 100644
index 00000000..131b7717
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php
@@ -0,0 +1,237 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Exception\BadResponseException;
+use GuzzleHttp\Exception\TooManyRedirectsException;
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Request redirect middleware.
+ *
+ * Apply this middleware like other middleware using
+ * {@see GuzzleHttp\Middleware::redirect()}.
+ */
+class RedirectMiddleware
+{
+ const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
+
+ const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
+
+ public static $defaultSettings = [
+ 'max' => 5,
+ 'protocols' => ['http', 'https'],
+ 'strict' => false,
+ 'referer' => false,
+ 'track_redirects' => false,
+ ];
+
+ /** @var callable */
+ private $nextHandler;
+
+ /**
+ * @param callable $nextHandler Next handler to invoke.
+ */
+ public function __construct(callable $nextHandler)
+ {
+ $this->nextHandler = $nextHandler;
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return PromiseInterface
+ */
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ $fn = $this->nextHandler;
+
+ if (empty($options['allow_redirects'])) {
+ return $fn($request, $options);
+ }
+
+ if ($options['allow_redirects'] === true) {
+ $options['allow_redirects'] = self::$defaultSettings;
+ } elseif (!is_array($options['allow_redirects'])) {
+ throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
+ } else {
+ // Merge the default settings with the provided settings
+ $options['allow_redirects'] += self::$defaultSettings;
+ }
+
+ if (empty($options['allow_redirects']['max'])) {
+ return $fn($request, $options);
+ }
+
+ return $fn($request, $options)
+ ->then(function (ResponseInterface $response) use ($request, $options) {
+ return $this->checkRedirect($request, $options, $response);
+ });
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param array $options
+ * @param ResponseInterface|PromiseInterface $response
+ *
+ * @return ResponseInterface|PromiseInterface
+ */
+ public function checkRedirect(
+ RequestInterface $request,
+ array $options,
+ ResponseInterface $response
+ ) {
+ if (substr($response->getStatusCode(), 0, 1) != '3'
+ || !$response->hasHeader('Location')
+ ) {
+ return $response;
+ }
+
+ $this->guardMax($request, $options);
+ $nextRequest = $this->modifyRequest($request, $options, $response);
+
+ if (isset($options['allow_redirects']['on_redirect'])) {
+ call_user_func(
+ $options['allow_redirects']['on_redirect'],
+ $request,
+ $response,
+ $nextRequest->getUri()
+ );
+ }
+
+ /** @var PromiseInterface|ResponseInterface $promise */
+ $promise = $this($nextRequest, $options);
+
+ // Add headers to be able to track history of redirects.
+ if (!empty($options['allow_redirects']['track_redirects'])) {
+ return $this->withTracking(
+ $promise,
+ (string) $nextRequest->getUri(),
+ $response->getStatusCode()
+ );
+ }
+
+ return $promise;
+ }
+
+ private function withTracking(PromiseInterface $promise, $uri, $statusCode)
+ {
+ return $promise->then(
+ function (ResponseInterface $response) use ($uri, $statusCode) {
+ // Note that we are pushing to the front of the list as this
+ // would be an earlier response than what is currently present
+ // in the history header.
+ $historyHeader = $response->getHeader(self::HISTORY_HEADER);
+ $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
+ array_unshift($historyHeader, $uri);
+ array_unshift($statusHeader, $statusCode);
+ return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
+ ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
+ }
+ );
+ }
+
+ private function guardMax(RequestInterface $request, array &$options)
+ {
+ $current = isset($options['__redirect_count'])
+ ? $options['__redirect_count']
+ : 0;
+ $options['__redirect_count'] = $current + 1;
+ $max = $options['allow_redirects']['max'];
+
+ if ($options['__redirect_count'] > $max) {
+ throw new TooManyRedirectsException(
+ "Will not follow more than {$max} redirects",
+ $request
+ );
+ }
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param array $options
+ * @param ResponseInterface $response
+ *
+ * @return RequestInterface
+ */
+ public function modifyRequest(
+ RequestInterface $request,
+ array $options,
+ ResponseInterface $response
+ ) {
+ // Request modifications to apply.
+ $modify = [];
+ $protocols = $options['allow_redirects']['protocols'];
+
+ // Use a GET request if this is an entity enclosing request and we are
+ // not forcing RFC compliance, but rather emulating what all browsers
+ // would do.
+ $statusCode = $response->getStatusCode();
+ if ($statusCode == 303 ||
+ ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
+ ) {
+ $modify['method'] = 'GET';
+ $modify['body'] = '';
+ }
+
+ $modify['uri'] = $this->redirectUri($request, $response, $protocols);
+ Psr7\rewind_body($request);
+
+ // Add the Referer header if it is told to do so and only
+ // add the header if we are not redirecting from https to http.
+ if ($options['allow_redirects']['referer']
+ && $modify['uri']->getScheme() === $request->getUri()->getScheme()
+ ) {
+ $uri = $request->getUri()->withUserInfo('', '');
+ $modify['set_headers']['Referer'] = (string) $uri;
+ } else {
+ $modify['remove_headers'][] = 'Referer';
+ }
+
+ // Remove Authorization header if host is different.
+ if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
+ $modify['remove_headers'][] = 'Authorization';
+ }
+
+ return Psr7\modify_request($request, $modify);
+ }
+
+ /**
+ * Set the appropriate URL on the request based on the location header
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @param array $protocols
+ *
+ * @return UriInterface
+ */
+ private function redirectUri(
+ RequestInterface $request,
+ ResponseInterface $response,
+ array $protocols
+ ) {
+ $location = Psr7\UriResolver::resolve(
+ $request->getUri(),
+ new Psr7\Uri($response->getHeaderLine('Location'))
+ );
+
+ // Ensure that the redirect URI is allowed based on the protocols.
+ if (!in_array($location->getScheme(), $protocols)) {
+ throw new BadResponseException(
+ sprintf(
+ 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
+ $location,
+ implode(', ', $protocols)
+ ),
+ $request,
+ $response
+ );
+ }
+
+ return $location;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/RequestOptions.php
new file mode 100644
index 00000000..c6aacfb1
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/RequestOptions.php
@@ -0,0 +1,255 @@
+<?php
+namespace GuzzleHttp;
+
+/**
+ * This class contains a list of built-in Guzzle request options.
+ *
+ * More documentation for each option can be found at http://guzzlephp.org/.
+ *
+ * @link http://docs.guzzlephp.org/en/v6/request-options.html
+ */
+final class RequestOptions
+{
+ /**
+ * allow_redirects: (bool|array) Controls redirect behavior. Pass false
+ * to disable redirects, pass true to enable redirects, pass an
+ * associative to provide custom redirect settings. Defaults to "false".
+ * This option only works if your handler has the RedirectMiddleware. When
+ * passing an associative array, you can provide the following key value
+ * pairs:
+ *
+ * - max: (int, default=5) maximum number of allowed redirects.
+ * - strict: (bool, default=false) Set to true to use strict redirects
+ * meaning redirect POST requests with POST requests vs. doing what most
+ * browsers do which is redirect POST requests with GET requests
+ * - referer: (bool, default=true) Set to false to disable the Referer
+ * header.
+ * - protocols: (array, default=['http', 'https']) Allowed redirect
+ * protocols.
+ * - on_redirect: (callable) PHP callable that is invoked when a redirect
+ * is encountered. The callable is invoked with the request, the redirect
+ * response that was received, and the effective URI. Any return value
+ * from the on_redirect function is ignored.
+ */
+ const ALLOW_REDIRECTS = 'allow_redirects';
+
+ /**
+ * auth: (array) Pass an array of HTTP authentication parameters to use
+ * with the request. The array must contain the username in index [0],
+ * the password in index [1], and you can optionally provide a built-in
+ * authentication type in index [2]. Pass null to disable authentication
+ * for a request.
+ */
+ const AUTH = 'auth';
+
+ /**
+ * body: (resource|string|null|int|float|StreamInterface|callable|\Iterator)
+ * Body to send in the request.
+ */
+ const BODY = 'body';
+
+ /**
+ * cert: (string|array) Set to a string to specify the path to a file
+ * containing a PEM formatted SSL client side certificate. If a password
+ * is required, then set cert to an array containing the path to the PEM
+ * file in the first array element followed by the certificate password
+ * in the second array element.
+ */
+ const CERT = 'cert';
+
+ /**
+ * cookies: (bool|GuzzleHttp\Cookie\CookieJarInterface, default=false)
+ * Specifies whether or not cookies are used in a request or what cookie
+ * jar to use or what cookies to send. This option only works if your
+ * handler has the `cookie` middleware. Valid values are `false` and
+ * an instance of {@see GuzzleHttp\Cookie\CookieJarInterface}.
+ */
+ const COOKIES = 'cookies';
+
+ /**
+ * connect_timeout: (float, default=0) Float describing the number of
+ * seconds to wait while trying to connect to a server. Use 0 to wait
+ * indefinitely (the default behavior).
+ */
+ const CONNECT_TIMEOUT = 'connect_timeout';
+
+ /**
+ * debug: (bool|resource) Set to true or set to a PHP stream returned by
+ * fopen() enable debug output with the HTTP handler used to send a
+ * request.
+ */
+ const DEBUG = 'debug';
+
+ /**
+ * decode_content: (bool, default=true) Specify whether or not
+ * Content-Encoding responses (gzip, deflate, etc.) are automatically
+ * decoded.
+ */
+ const DECODE_CONTENT = 'decode_content';
+
+ /**
+ * delay: (int) The amount of time to delay before sending in milliseconds.
+ */
+ const DELAY = 'delay';
+
+ /**
+ * expect: (bool|integer) Controls the behavior of the
+ * "Expect: 100-Continue" header.
+ *
+ * Set to `true` to enable the "Expect: 100-Continue" header for all
+ * requests that sends a body. Set to `false` to disable the
+ * "Expect: 100-Continue" header for all requests. Set to a number so that
+ * the size of the payload must be greater than the number in order to send
+ * the Expect header. Setting to a number will send the Expect header for
+ * all requests in which the size of the payload cannot be determined or
+ * where the body is not rewindable.
+ *
+ * By default, Guzzle will add the "Expect: 100-Continue" header when the
+ * size of the body of a request is greater than 1 MB and a request is
+ * using HTTP/1.1.
+ */
+ const EXPECT = 'expect';
+
+ /**
+ * form_params: (array) Associative array of form field names to values
+ * where each value is a string or array of strings. Sets the Content-Type
+ * header to application/x-www-form-urlencoded when no Content-Type header
+ * is already present.
+ */
+ const FORM_PARAMS = 'form_params';
+
+ /**
+ * headers: (array) Associative array of HTTP headers. Each value MUST be
+ * a string or array of strings.
+ */
+ const HEADERS = 'headers';
+
+ /**
+ * http_errors: (bool, default=true) Set to false to disable exceptions
+ * when a non- successful HTTP response is received. By default,
+ * exceptions will be thrown for 4xx and 5xx responses. This option only
+ * works if your handler has the `httpErrors` middleware.
+ */
+ const HTTP_ERRORS = 'http_errors';
+
+ /**
+ * json: (mixed) Adds JSON data to a request. The provided value is JSON
+ * encoded and a Content-Type header of application/json will be added to
+ * the request if no Content-Type header is already present.
+ */
+ const JSON = 'json';
+
+ /**
+ * multipart: (array) Array of associative arrays, each containing a
+ * required "name" key mapping to the form field, name, a required
+ * "contents" key mapping to a StreamInterface|resource|string, an
+ * optional "headers" associative array of custom headers, and an
+ * optional "filename" key mapping to a string to send as the filename in
+ * the part. If no "filename" key is present, then no "filename" attribute
+ * will be added to the part.
+ */
+ const MULTIPART = 'multipart';
+
+ /**
+ * on_headers: (callable) A callable that is invoked when the HTTP headers
+ * of the response have been received but the body has not yet begun to
+ * download.
+ */
+ const ON_HEADERS = 'on_headers';
+
+ /**
+ * on_stats: (callable) allows you to get access to transfer statistics of
+ * a request and access the lower level transfer details of the handler
+ * associated with your client. ``on_stats`` is a callable that is invoked
+ * when a handler has finished sending a request. The callback is invoked
+ * with transfer statistics about the request, the response received, or
+ * the error encountered. Included in the data is the total amount of time
+ * taken to send the request.
+ */
+ const ON_STATS = 'on_stats';
+
+ /**
+ * progress: (callable) Defines a function to invoke when transfer
+ * progress is made. The function accepts the following positional
+ * arguments: the total number of bytes expected to be downloaded, the
+ * number of bytes downloaded so far, the number of bytes expected to be
+ * uploaded, the number of bytes uploaded so far.
+ */
+ const PROGRESS = 'progress';
+
+ /**
+ * proxy: (string|array) Pass a string to specify an HTTP proxy, or an
+ * array to specify different proxies for different protocols (where the
+ * key is the protocol and the value is a proxy string).
+ */
+ const PROXY = 'proxy';
+
+ /**
+ * query: (array|string) Associative array of query string values to add
+ * to the request. This option uses PHP's http_build_query() to create
+ * the string representation. Pass a string value if you need more
+ * control than what this method provides
+ */
+ const QUERY = 'query';
+
+ /**
+ * sink: (resource|string|StreamInterface) Where the data of the
+ * response is written to. Defaults to a PHP temp stream. Providing a
+ * string will write data to a file by the given name.
+ */
+ const SINK = 'sink';
+
+ /**
+ * synchronous: (bool) Set to true to inform HTTP handlers that you intend
+ * on waiting on the response. This can be useful for optimizations. Note
+ * that a promise is still returned if you are using one of the async
+ * client methods.
+ */
+ const SYNCHRONOUS = 'synchronous';
+
+ /**
+ * ssl_key: (array|string) Specify the path to a file containing a private
+ * SSL key in PEM format. If a password is required, then set to an array
+ * containing the path to the SSL key in the first array element followed
+ * by the password required for the certificate in the second element.
+ */
+ const SSL_KEY = 'ssl_key';
+
+ /**
+ * stream: Set to true to attempt to stream a response rather than
+ * download it all up-front.
+ */
+ const STREAM = 'stream';
+
+ /**
+ * verify: (bool|string, default=true) Describes the SSL certificate
+ * verification behavior of a request. Set to true to enable SSL
+ * certificate verification using the system CA bundle when available
+ * (the default). Set to false to disable certificate verification (this
+ * is insecure!). Set to a string to provide the path to a CA bundle on
+ * disk to enable verification using a custom certificate.
+ */
+ const VERIFY = 'verify';
+
+ /**
+ * timeout: (float, default=0) Float describing the timeout of the
+ * request in seconds. Use 0 to wait indefinitely (the default behavior).
+ */
+ const TIMEOUT = 'timeout';
+
+ /**
+ * read_timeout: (float, default=default_socket_timeout ini setting) Float describing
+ * the body read timeout, for stream requests.
+ */
+ const READ_TIMEOUT = 'read_timeout';
+
+ /**
+ * version: (float) Specifies the HTTP protocol version to attempt to use.
+ */
+ const VERSION = 'version';
+
+ /**
+ * force_ip_resolve: (bool) Force client to use only ipv4 or ipv6 protocol
+ */
+ const FORCE_IP_RESOLVE = 'force_ip_resolve';
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
new file mode 100644
index 00000000..f27090fd
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php
@@ -0,0 +1,112 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Promise\PromiseInterface;
+use GuzzleHttp\Promise\RejectedPromise;
+use GuzzleHttp\Psr7;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * Middleware that retries requests based on the boolean result of
+ * invoking the provided "decider" function.
+ */
+class RetryMiddleware
+{
+ /** @var callable */
+ private $nextHandler;
+
+ /** @var callable */
+ private $decider;
+
+ /**
+ * @param callable $decider Function that accepts the number of retries,
+ * a request, [response], and [exception] and
+ * returns true if the request is to be
+ * retried.
+ * @param callable $nextHandler Next handler to invoke.
+ * @param callable $delay Function that accepts the number of retries
+ * and [response] and returns the number of
+ * milliseconds to delay.
+ */
+ public function __construct(
+ callable $decider,
+ callable $nextHandler,
+ callable $delay = null
+ ) {
+ $this->decider = $decider;
+ $this->nextHandler = $nextHandler;
+ $this->delay = $delay ?: __CLASS__ . '::exponentialDelay';
+ }
+
+ /**
+ * Default exponential backoff delay function.
+ *
+ * @param $retries
+ *
+ * @return int
+ */
+ public static function exponentialDelay($retries)
+ {
+ return (int) pow(2, $retries - 1);
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return PromiseInterface
+ */
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ if (!isset($options['retries'])) {
+ $options['retries'] = 0;
+ }
+
+ $fn = $this->nextHandler;
+ return $fn($request, $options)
+ ->then(
+ $this->onFulfilled($request, $options),
+ $this->onRejected($request, $options)
+ );
+ }
+
+ private function onFulfilled(RequestInterface $req, array $options)
+ {
+ return function ($value) use ($req, $options) {
+ if (!call_user_func(
+ $this->decider,
+ $options['retries'],
+ $req,
+ $value,
+ null
+ )) {
+ return $value;
+ }
+ return $this->doRetry($req, $options, $value);
+ };
+ }
+
+ private function onRejected(RequestInterface $req, array $options)
+ {
+ return function ($reason) use ($req, $options) {
+ if (!call_user_func(
+ $this->decider,
+ $options['retries'],
+ $req,
+ null,
+ $reason
+ )) {
+ return \GuzzleHttp\Promise\rejection_for($reason);
+ }
+ return $this->doRetry($req, $options);
+ };
+ }
+
+ private function doRetry(RequestInterface $request, array $options, ResponseInterface $response = null)
+ {
+ $options['delay'] = call_user_func($this->delay, ++$options['retries'], $response);
+
+ return $this($request, $options);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/TransferStats.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/TransferStats.php
new file mode 100644
index 00000000..15f717e1
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/TransferStats.php
@@ -0,0 +1,126 @@
+<?php
+namespace GuzzleHttp;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Represents data at the point after it was transferred either successfully
+ * or after a network error.
+ */
+final class TransferStats
+{
+ private $request;
+ private $response;
+ private $transferTime;
+ private $handlerStats;
+ private $handlerErrorData;
+
+ /**
+ * @param RequestInterface $request Request that was sent.
+ * @param ResponseInterface $response Response received (if any)
+ * @param null $transferTime Total handler transfer time.
+ * @param mixed $handlerErrorData Handler error data.
+ * @param array $handlerStats Handler specific stats.
+ */
+ public function __construct(
+ RequestInterface $request,
+ ResponseInterface $response = null,
+ $transferTime = null,
+ $handlerErrorData = null,
+ $handlerStats = []
+ ) {
+ $this->request = $request;
+ $this->response = $response;
+ $this->transferTime = $transferTime;
+ $this->handlerErrorData = $handlerErrorData;
+ $this->handlerStats = $handlerStats;
+ }
+
+ /**
+ * @return RequestInterface
+ */
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ /**
+ * Returns the response that was received (if any).
+ *
+ * @return ResponseInterface|null
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+
+ /**
+ * Returns true if a response was received.
+ *
+ * @return bool
+ */
+ public function hasResponse()
+ {
+ return $this->response !== null;
+ }
+
+ /**
+ * Gets handler specific error data.
+ *
+ * This might be an exception, a integer representing an error code, or
+ * anything else. Relying on this value assumes that you know what handler
+ * you are using.
+ *
+ * @return mixed
+ */
+ public function getHandlerErrorData()
+ {
+ return $this->handlerErrorData;
+ }
+
+ /**
+ * Get the effective URI the request was sent to.
+ *
+ * @return UriInterface
+ */
+ public function getEffectiveUri()
+ {
+ return $this->request->getUri();
+ }
+
+ /**
+ * Get the estimated time the request was being transferred by the handler.
+ *
+ * @return float Time in seconds.
+ */
+ public function getTransferTime()
+ {
+ return $this->transferTime;
+ }
+
+ /**
+ * Gets an array of all of the handler specific transfer data.
+ *
+ * @return array
+ */
+ public function getHandlerStats()
+ {
+ return $this->handlerStats;
+ }
+
+ /**
+ * Get a specific handler statistic from the handler by name.
+ *
+ * @param string $stat Handler specific transfer stat to retrieve.
+ *
+ * @return mixed|null
+ */
+ public function getHandlerStat($stat)
+ {
+ return isset($this->handlerStats[$stat])
+ ? $this->handlerStats[$stat]
+ : null;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/UriTemplate.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/UriTemplate.php
new file mode 100644
index 00000000..96dcfd09
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/UriTemplate.php
@@ -0,0 +1,237 @@
+<?php
+namespace GuzzleHttp;
+
+/**
+ * Expands URI templates. Userland implementation of PECL uri_template.
+ *
+ * @link http://tools.ietf.org/html/rfc6570
+ */
+class UriTemplate
+{
+ /** @var string URI template */
+ private $template;
+
+ /** @var array Variables to use in the template expansion */
+ private $variables;
+
+ /** @var array Hash for quick operator lookups */
+ private static $operatorHash = [
+ '' => ['prefix' => '', 'joiner' => ',', 'query' => false],
+ '+' => ['prefix' => '', 'joiner' => ',', 'query' => false],
+ '#' => ['prefix' => '#', 'joiner' => ',', 'query' => false],
+ '.' => ['prefix' => '.', 'joiner' => '.', 'query' => false],
+ '/' => ['prefix' => '/', 'joiner' => '/', 'query' => false],
+ ';' => ['prefix' => ';', 'joiner' => ';', 'query' => true],
+ '?' => ['prefix' => '?', 'joiner' => '&', 'query' => true],
+ '&' => ['prefix' => '&', 'joiner' => '&', 'query' => true]
+ ];
+
+ /** @var array Delimiters */
+ private static $delims = [':', '/', '?', '#', '[', ']', '@', '!', '$',
+ '&', '\'', '(', ')', '*', '+', ',', ';', '='];
+
+ /** @var array Percent encoded delimiters */
+ private static $delimsPct = ['%3A', '%2F', '%3F', '%23', '%5B', '%5D',
+ '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C',
+ '%3B', '%3D'];
+
+ public function expand($template, array $variables)
+ {
+ if (false === strpos($template, '{')) {
+ return $template;
+ }
+
+ $this->template = $template;
+ $this->variables = $variables;
+
+ return preg_replace_callback(
+ '/\{([^\}]+)\}/',
+ [$this, 'expandMatch'],
+ $this->template
+ );
+ }
+
+ /**
+ * Parse an expression into parts
+ *
+ * @param string $expression Expression to parse
+ *
+ * @return array Returns an associative array of parts
+ */
+ private function parseExpression($expression)
+ {
+ $result = [];
+
+ if (isset(self::$operatorHash[$expression[0]])) {
+ $result['operator'] = $expression[0];
+ $expression = substr($expression, 1);
+ } else {
+ $result['operator'] = '';
+ }
+
+ foreach (explode(',', $expression) as $value) {
+ $value = trim($value);
+ $varspec = [];
+ if ($colonPos = strpos($value, ':')) {
+ $varspec['value'] = substr($value, 0, $colonPos);
+ $varspec['modifier'] = ':';
+ $varspec['position'] = (int) substr($value, $colonPos + 1);
+ } elseif (substr($value, -1) === '*') {
+ $varspec['modifier'] = '*';
+ $varspec['value'] = substr($value, 0, -1);
+ } else {
+ $varspec['value'] = (string) $value;
+ $varspec['modifier'] = '';
+ }
+ $result['values'][] = $varspec;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Process an expansion
+ *
+ * @param array $matches Matches met in the preg_replace_callback
+ *
+ * @return string Returns the replacement string
+ */
+ private function expandMatch(array $matches)
+ {
+ static $rfc1738to3986 = ['+' => '%20', '%7e' => '~'];
+
+ $replacements = [];
+ $parsed = self::parseExpression($matches[1]);
+ $prefix = self::$operatorHash[$parsed['operator']]['prefix'];
+ $joiner = self::$operatorHash[$parsed['operator']]['joiner'];
+ $useQuery = self::$operatorHash[$parsed['operator']]['query'];
+
+ foreach ($parsed['values'] as $value) {
+ if (!isset($this->variables[$value['value']])) {
+ continue;
+ }
+
+ $variable = $this->variables[$value['value']];
+ $actuallyUseQuery = $useQuery;
+ $expanded = '';
+
+ if (is_array($variable)) {
+ $isAssoc = $this->isAssoc($variable);
+ $kvp = [];
+ foreach ($variable as $key => $var) {
+ if ($isAssoc) {
+ $key = rawurlencode($key);
+ $isNestedArray = is_array($var);
+ } else {
+ $isNestedArray = false;
+ }
+
+ if (!$isNestedArray) {
+ $var = rawurlencode($var);
+ if ($parsed['operator'] === '+' ||
+ $parsed['operator'] === '#'
+ ) {
+ $var = $this->decodeReserved($var);
+ }
+ }
+
+ if ($value['modifier'] === '*') {
+ if ($isAssoc) {
+ if ($isNestedArray) {
+ // Nested arrays must allow for deeply nested
+ // structures.
+ $var = strtr(
+ http_build_query([$key => $var]),
+ $rfc1738to3986
+ );
+ } else {
+ $var = $key . '=' . $var;
+ }
+ } elseif ($key > 0 && $actuallyUseQuery) {
+ $var = $value['value'] . '=' . $var;
+ }
+ }
+
+ $kvp[$key] = $var;
+ }
+
+ if (empty($variable)) {
+ $actuallyUseQuery = false;
+ } elseif ($value['modifier'] === '*') {
+ $expanded = implode($joiner, $kvp);
+ if ($isAssoc) {
+ // Don't prepend the value name when using the explode
+ // modifier with an associative array.
+ $actuallyUseQuery = false;
+ }
+ } else {
+ if ($isAssoc) {
+ // When an associative array is encountered and the
+ // explode modifier is not set, then the result must be
+ // a comma separated list of keys followed by their
+ // respective values.
+ foreach ($kvp as $k => &$v) {
+ $v = $k . ',' . $v;
+ }
+ }
+ $expanded = implode(',', $kvp);
+ }
+ } else {
+ if ($value['modifier'] === ':') {
+ $variable = substr($variable, 0, $value['position']);
+ }
+ $expanded = rawurlencode($variable);
+ if ($parsed['operator'] === '+' || $parsed['operator'] === '#') {
+ $expanded = $this->decodeReserved($expanded);
+ }
+ }
+
+ if ($actuallyUseQuery) {
+ if (!$expanded && $joiner !== '&') {
+ $expanded = $value['value'];
+ } else {
+ $expanded = $value['value'] . '=' . $expanded;
+ }
+ }
+
+ $replacements[] = $expanded;
+ }
+
+ $ret = implode($joiner, $replacements);
+ if ($ret && $prefix) {
+ return $prefix . $ret;
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Determines if an array is associative.
+ *
+ * This makes the assumption that input arrays are sequences or hashes.
+ * This assumption is a tradeoff for accuracy in favor of speed, but it
+ * should work in almost every case where input is supplied for a URI
+ * template.
+ *
+ * @param array $array Array to check
+ *
+ * @return bool
+ */
+ private function isAssoc(array $array)
+ {
+ return $array && array_keys($array)[0] !== 0;
+ }
+
+ /**
+ * Removes percent encoding on reserved characters (used with + and #
+ * modifiers).
+ *
+ * @param string $string String to fix
+ *
+ * @return string
+ */
+ private function decodeReserved($string)
+ {
+ return str_replace(self::$delimsPct, self::$delims, $string);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/functions.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/functions.php
new file mode 100644
index 00000000..a3ac450d
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/functions.php
@@ -0,0 +1,333 @@
+<?php
+namespace GuzzleHttp;
+
+use GuzzleHttp\Handler\CurlHandler;
+use GuzzleHttp\Handler\CurlMultiHandler;
+use GuzzleHttp\Handler\Proxy;
+use GuzzleHttp\Handler\StreamHandler;
+
+/**
+ * Expands a URI template
+ *
+ * @param string $template URI template
+ * @param array $variables Template variables
+ *
+ * @return string
+ */
+function uri_template($template, array $variables)
+{
+ if (extension_loaded('uri_template')) {
+ // @codeCoverageIgnoreStart
+ return \uri_template($template, $variables);
+ // @codeCoverageIgnoreEnd
+ }
+
+ static $uriTemplate;
+ if (!$uriTemplate) {
+ $uriTemplate = new UriTemplate();
+ }
+
+ return $uriTemplate->expand($template, $variables);
+}
+
+/**
+ * Debug function used to describe the provided value type and class.
+ *
+ * @param mixed $input
+ *
+ * @return string Returns a string containing the type of the variable and
+ * if a class is provided, the class name.
+ */
+function describe_type($input)
+{
+ switch (gettype($input)) {
+ case 'object':
+ return 'object(' . get_class($input) . ')';
+ case 'array':
+ return 'array(' . count($input) . ')';
+ default:
+ ob_start();
+ var_dump($input);
+ // normalize float vs double
+ return str_replace('double(', 'float(', rtrim(ob_get_clean()));
+ }
+}
+
+/**
+ * Parses an array of header lines into an associative array of headers.
+ *
+ * @param array $lines Header lines array of strings in the following
+ * format: "Name: Value"
+ * @return array
+ */
+function headers_from_lines($lines)
+{
+ $headers = [];
+
+ foreach ($lines as $line) {
+ $parts = explode(':', $line, 2);
+ $headers[trim($parts[0])][] = isset($parts[1])
+ ? trim($parts[1])
+ : null;
+ }
+
+ return $headers;
+}
+
+/**
+ * Returns a debug stream based on the provided variable.
+ *
+ * @param mixed $value Optional value
+ *
+ * @return resource
+ */
+function debug_resource($value = null)
+{
+ if (is_resource($value)) {
+ return $value;
+ } elseif (defined('STDOUT')) {
+ return STDOUT;
+ }
+
+ return fopen('php://output', 'w');
+}
+
+/**
+ * Chooses and creates a default handler to use based on the environment.
+ *
+ * The returned handler is not wrapped by any default middlewares.
+ *
+ * @throws \RuntimeException if no viable Handler is available.
+ * @return callable Returns the best handler for the given system.
+ */
+function choose_handler()
+{
+ $handler = null;
+ if (function_exists('curl_multi_exec') && function_exists('curl_exec')) {
+ $handler = Proxy::wrapSync(new CurlMultiHandler(), new CurlHandler());
+ } elseif (function_exists('curl_exec')) {
+ $handler = new CurlHandler();
+ } elseif (function_exists('curl_multi_exec')) {
+ $handler = new CurlMultiHandler();
+ }
+
+ if (ini_get('allow_url_fopen')) {
+ $handler = $handler
+ ? Proxy::wrapStreaming($handler, new StreamHandler())
+ : new StreamHandler();
+ } elseif (!$handler) {
+ throw new \RuntimeException('GuzzleHttp requires cURL, the '
+ . 'allow_url_fopen ini setting, or a custom HTTP handler.');
+ }
+
+ return $handler;
+}
+
+/**
+ * Get the default User-Agent string to use with Guzzle
+ *
+ * @return string
+ */
+function default_user_agent()
+{
+ static $defaultAgent = '';
+
+ if (!$defaultAgent) {
+ $defaultAgent = 'GuzzleHttp/' . Client::VERSION;
+ if (extension_loaded('curl') && function_exists('curl_version')) {
+ $defaultAgent .= ' curl/' . \curl_version()['version'];
+ }
+ $defaultAgent .= ' PHP/' . PHP_VERSION;
+ }
+
+ return $defaultAgent;
+}
+
+/**
+ * Returns the default cacert bundle for the current system.
+ *
+ * First, the openssl.cafile and curl.cainfo php.ini settings are checked.
+ * If those settings are not configured, then the common locations for
+ * bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
+ * and Windows are checked. If any of these file locations are found on
+ * disk, they will be utilized.
+ *
+ * Note: the result of this function is cached for subsequent calls.
+ *
+ * @return string
+ * @throws \RuntimeException if no bundle can be found.
+ */
+function default_ca_bundle()
+{
+ static $cached = null;
+ static $cafiles = [
+ // Red Hat, CentOS, Fedora (provided by the ca-certificates package)
+ '/etc/pki/tls/certs/ca-bundle.crt',
+ // Ubuntu, Debian (provided by the ca-certificates package)
+ '/etc/ssl/certs/ca-certificates.crt',
+ // FreeBSD (provided by the ca_root_nss package)
+ '/usr/local/share/certs/ca-root-nss.crt',
+ // SLES 12 (provided by the ca-certificates package)
+ '/var/lib/ca-certificates/ca-bundle.pem',
+ // OS X provided by homebrew (using the default path)
+ '/usr/local/etc/openssl/cert.pem',
+ // Google app engine
+ '/etc/ca-certificates.crt',
+ // Windows?
+ 'C:\\windows\\system32\\curl-ca-bundle.crt',
+ 'C:\\windows\\curl-ca-bundle.crt',
+ ];
+
+ if ($cached) {
+ return $cached;
+ }
+
+ if ($ca = ini_get('openssl.cafile')) {
+ return $cached = $ca;
+ }
+
+ if ($ca = ini_get('curl.cainfo')) {
+ return $cached = $ca;
+ }
+
+ foreach ($cafiles as $filename) {
+ if (file_exists($filename)) {
+ return $cached = $filename;
+ }
+ }
+
+ throw new \RuntimeException(<<< EOT
+No system CA bundle could be found in any of the the common system locations.
+PHP versions earlier than 5.6 are not properly configured to use the system's
+CA bundle by default. In order to verify peer certificates, you will need to
+supply the path on disk to a certificate bundle to the 'verify' request
+option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
+need a specific certificate bundle, then Mozilla provides a commonly used CA
+bundle which can be downloaded here (provided by the maintainer of cURL):
+https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
+you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
+ini setting to point to the path to the file, allowing you to omit the 'verify'
+request option. See http://curl.haxx.se/docs/sslcerts.html for more
+information.
+EOT
+ );
+}
+
+/**
+ * Creates an associative array of lowercase header names to the actual
+ * header casing.
+ *
+ * @param array $headers
+ *
+ * @return array
+ */
+function normalize_header_keys(array $headers)
+{
+ $result = [];
+ foreach (array_keys($headers) as $key) {
+ $result[strtolower($key)] = $key;
+ }
+
+ return $result;
+}
+
+/**
+ * Returns true if the provided host matches any of the no proxy areas.
+ *
+ * This method will strip a port from the host if it is present. Each pattern
+ * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a
+ * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" ==
+ * "baz.foo.com", but ".foo.com" != "foo.com").
+ *
+ * Areas are matched in the following cases:
+ * 1. "*" (without quotes) always matches any hosts.
+ * 2. An exact match.
+ * 3. The area starts with "." and the area is the last part of the host. e.g.
+ * '.mit.edu' will match any host that ends with '.mit.edu'.
+ *
+ * @param string $host Host to check against the patterns.
+ * @param array $noProxyArray An array of host patterns.
+ *
+ * @return bool
+ */
+function is_host_in_noproxy($host, array $noProxyArray)
+{
+ if (strlen($host) === 0) {
+ throw new \InvalidArgumentException('Empty host provided');
+ }
+
+ // Strip port if present.
+ if (strpos($host, ':')) {
+ $host = explode($host, ':', 2)[0];
+ }
+
+ foreach ($noProxyArray as $area) {
+ // Always match on wildcards.
+ if ($area === '*') {
+ return true;
+ } elseif (empty($area)) {
+ // Don't match on empty values.
+ continue;
+ } elseif ($area === $host) {
+ // Exact matches.
+ return true;
+ } else {
+ // Special match if the area when prefixed with ".". Remove any
+ // existing leading "." and add a new leading ".".
+ $area = '.' . ltrim($area, '.');
+ if (substr($host, -(strlen($area))) === $area) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Wrapper for json_decode that throws when an error occurs.
+ *
+ * @param string $json JSON data to parse
+ * @param bool $assoc When true, returned objects will be converted
+ * into associative arrays.
+ * @param int $depth User specified recursion depth.
+ * @param int $options Bitmask of JSON decode options.
+ *
+ * @return mixed
+ * @throws \InvalidArgumentException if the JSON cannot be decoded.
+ * @link http://www.php.net/manual/en/function.json-decode.php
+ */
+function json_decode($json, $assoc = false, $depth = 512, $options = 0)
+{
+ $data = \json_decode($json, $assoc, $depth, $options);
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new \InvalidArgumentException(
+ 'json_decode error: ' . json_last_error_msg()
+ );
+ }
+
+ return $data;
+}
+
+/**
+ * Wrapper for JSON encoding that throws when an error occurs.
+ *
+ * @param mixed $value The value being encoded
+ * @param int $options JSON encode option bitmask
+ * @param int $depth Set the maximum depth. Must be greater than zero.
+ *
+ * @return string
+ * @throws \InvalidArgumentException if the JSON cannot be encoded.
+ * @link http://www.php.net/manual/en/function.json-encode.php
+ */
+function json_encode($value, $options = 0, $depth = 512)
+{
+ $json = \json_encode($value, $options, $depth);
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new \InvalidArgumentException(
+ 'json_encode error: ' . json_last_error_msg()
+ );
+ }
+
+ return $json;
+}
diff --git a/bin/wiki/vendor/guzzlehttp/guzzle/src/functions_include.php b/bin/wiki/vendor/guzzlehttp/guzzle/src/functions_include.php
new file mode 100644
index 00000000..a93393ac
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/guzzle/src/functions_include.php
@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\uri_template')) {
+ require __DIR__ . '/functions.php';
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/CHANGELOG.md b/bin/wiki/vendor/guzzlehttp/promises/CHANGELOG.md
new file mode 100644
index 00000000..551929f6
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/CHANGELOG.md
@@ -0,0 +1,65 @@
+# CHANGELOG
+
+
+## 1.3.1 - 2016-12-20
+
+### Fixed
+
+- `wait()` foreign promise compatibility
+
+
+## 1.3.0 - 2016-11-18
+
+### Added
+
+- Adds support for custom task queues.
+
+### Fixed
+
+- Fixed coroutine promise memory leak.
+
+
+## 1.2.0 - 2016-05-18
+
+### Changed
+
+- Update to now catch `\Throwable` on PHP 7+
+
+
+## 1.1.0 - 2016-03-07
+
+### Changed
+
+- Update EachPromise to prevent recurring on a iterator when advancing, as this
+ could trigger fatal generator errors.
+- Update Promise to allow recursive waiting without unwrapping exceptions.
+
+
+## 1.0.3 - 2015-10-15
+
+### Changed
+
+- Update EachPromise to immediately resolve when the underlying promise iterator
+ is empty. Previously, such a promise would throw an exception when its `wait`
+ function was called.
+
+
+## 1.0.2 - 2015-05-15
+
+### Changed
+
+- Conditionally require functions.php.
+
+
+## 1.0.1 - 2015-06-24
+
+### Changed
+
+- Updating EachPromise to call next on the underlying promise iterator as late
+ as possible to ensure that generators that generate new requests based on
+ callbacks are not iterated until after callbacks are invoked.
+
+
+## 1.0.0 - 2015-05-12
+
+- Initial release
diff --git a/bin/wiki/vendor/guzzlehttp/promises/LICENSE b/bin/wiki/vendor/guzzlehttp/promises/LICENSE
new file mode 100644
index 00000000..67f91a14
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2016 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/bin/wiki/vendor/guzzlehttp/promises/Makefile b/bin/wiki/vendor/guzzlehttp/promises/Makefile
new file mode 100644
index 00000000..8d5b3ef9
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/Makefile
@@ -0,0 +1,13 @@
+all: clean test
+
+test:
+ vendor/bin/phpunit
+
+coverage:
+ vendor/bin/phpunit --coverage-html=artifacts/coverage
+
+view-coverage:
+ open artifacts/coverage/index.html
+
+clean:
+ rm -rf artifacts/*
diff --git a/bin/wiki/vendor/guzzlehttp/promises/README.md b/bin/wiki/vendor/guzzlehttp/promises/README.md
new file mode 100644
index 00000000..7b607e28
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/README.md
@@ -0,0 +1,504 @@
+# Guzzle Promises
+
+[Promises/A+](https://promisesaplus.com/) implementation that handles promise
+chaining and resolution iteratively, allowing for "infinite" promise chaining
+while keeping the stack size constant. Read [this blog post](https://blog.domenic.me/youre-missing-the-point-of-promises/)
+for a general introduction to promises.
+
+- [Features](#features)
+- [Quick start](#quick-start)
+- [Synchronous wait](#synchronous-wait)
+- [Cancellation](#cancellation)
+- [API](#api)
+ - [Promise](#promise)
+ - [FulfilledPromise](#fulfilledpromise)
+ - [RejectedPromise](#rejectedpromise)
+- [Promise interop](#promise-interop)
+- [Implementation notes](#implementation-notes)
+
+
+# Features
+
+- [Promises/A+](https://promisesaplus.com/) implementation.
+- Promise resolution and chaining is handled iteratively, allowing for
+ "infinite" promise chaining.
+- Promises have a synchronous `wait` method.
+- Promises can be cancelled.
+- Works with any object that has a `then` function.
+- C# style async/await coroutine promises using
+ `GuzzleHttp\Promise\coroutine()`.
+
+
+# Quick start
+
+A *promise* represents the eventual result of an asynchronous operation. The
+primary way of interacting with a promise is through its `then` method, which
+registers callbacks to receive either a promise's eventual value or the reason
+why the promise cannot be fulfilled.
+
+
+## Callbacks
+
+Callbacks are registered with the `then` method by providing an optional
+`$onFulfilled` followed by an optional `$onRejected` function.
+
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(
+ // $onFulfilled
+ function ($value) {
+ echo 'The promise was fulfilled.';
+ },
+ // $onRejected
+ function ($reason) {
+ echo 'The promise was rejected.';
+ }
+);
+```
+
+*Resolving* a promise means that you either fulfill a promise with a *value* or
+reject a promise with a *reason*. Resolving a promises triggers callbacks
+registered with the promises's `then` method. These callbacks are triggered
+only once and in the order in which they were added.
+
+
+## Resolving a promise
+
+Promises are fulfilled using the `resolve($value)` method. Resolving a promise
+with any value other than a `GuzzleHttp\Promise\RejectedPromise` will trigger
+all of the onFulfilled callbacks (resolving a promise with a rejected promise
+will reject the promise and trigger the `$onRejected` callbacks).
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise
+ ->then(function ($value) {
+ // Return a value and don't break the chain
+ return "Hello, " . $value;
+ })
+ // This then is executed after the first then and receives the value
+ // returned from the first then.
+ ->then(function ($value) {
+ echo $value;
+ });
+
+// Resolving the promise triggers the $onFulfilled callbacks and outputs
+// "Hello, reader".
+$promise->resolve('reader.');
+```
+
+
+## Promise forwarding
+
+Promises can be chained one after the other. Each then in the chain is a new
+promise. The return value of a promise is what's forwarded to the next
+promise in the chain. Returning a promise in a `then` callback will cause the
+subsequent promises in the chain to only be fulfilled when the returned promise
+has been fulfilled. The next promise in the chain will be invoked with the
+resolved value of the promise.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$nextPromise = new Promise();
+
+$promise
+ ->then(function ($value) use ($nextPromise) {
+ echo $value;
+ return $nextPromise;
+ })
+ ->then(function ($value) {
+ echo $value;
+ });
+
+// Triggers the first callback and outputs "A"
+$promise->resolve('A');
+// Triggers the second callback and outputs "B"
+$nextPromise->resolve('B');
+```
+
+## Promise rejection
+
+When a promise is rejected, the `$onRejected` callbacks are invoked with the
+rejection reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+ echo $reason;
+});
+
+$promise->reject('Error!');
+// Outputs "Error!"
+```
+
+## Rejection forwarding
+
+If an exception is thrown in an `$onRejected` callback, subsequent
+`$onRejected` callbacks are invoked with the thrown exception as the reason.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+ throw new \Exception($reason);
+})->then(null, function ($reason) {
+ assert($reason->getMessage() === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+You can also forward a rejection down the promise chain by returning a
+`GuzzleHttp\Promise\RejectedPromise` in either an `$onFulfilled` or
+`$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise->then(null, function ($reason) {
+ return new RejectedPromise($reason);
+})->then(null, function ($reason) {
+ assert($reason === 'Error!');
+});
+
+$promise->reject('Error!');
+```
+
+If an exception is not thrown in a `$onRejected` callback and the callback
+does not return a rejected promise, downstream `$onFulfilled` callbacks are
+invoked using the value returned from the `$onRejected` callback.
+
+```php
+use GuzzleHttp\Promise\Promise;
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new Promise();
+$promise
+ ->then(null, function ($reason) {
+ return "It's ok";
+ })
+ ->then(function ($value) {
+ assert($value === "It's ok");
+ });
+
+$promise->reject('Error!');
+```
+
+# Synchronous wait
+
+You can synchronously force promises to complete using a promise's `wait`
+method. When creating a promise, you can provide a wait function that is used
+to synchronously force a promise to complete. When a wait function is invoked
+it is expected to deliver a value to the promise or reject the promise. If the
+wait function does not deliver a value, then an exception is thrown. The wait
+function provided to a promise constructor is invoked when the `wait` function
+of the promise is called.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+ $promise->resolve('foo');
+});
+
+// Calling wait will return the value of the promise.
+echo $promise->wait(); // outputs "foo"
+```
+
+If an exception is encountered while invoking the wait function of a promise,
+the promise is rejected with the exception and the exception is thrown.
+
+```php
+$promise = new Promise(function () use (&$promise) {
+ throw new \Exception('foo');
+});
+
+$promise->wait(); // throws the exception.
+```
+
+Calling `wait` on a promise that has been fulfilled will not trigger the wait
+function. It will simply return the previously resolved value.
+
+```php
+$promise = new Promise(function () { die('this is not called!'); });
+$promise->resolve('foo');
+echo $promise->wait(); // outputs "foo"
+```
+
+Calling `wait` on a promise that has been rejected will throw an exception. If
+the rejection reason is an instance of `\Exception` the reason is thrown.
+Otherwise, a `GuzzleHttp\Promise\RejectionException` is thrown and the reason
+can be obtained by calling the `getReason` method of the exception.
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+$promise->wait();
+```
+
+> PHP Fatal error: Uncaught exception 'GuzzleHttp\Promise\RejectionException' with message 'The promise was rejected with value: foo'
+
+
+## Unwrapping a promise
+
+When synchronously waiting on a promise, you are joining the state of the
+promise into the current state of execution (i.e., return the value of the
+promise if it was fulfilled or throw an exception if it was rejected). This is
+called "unwrapping" the promise. Waiting on a promise will by default unwrap
+the promise state.
+
+You can force a promise to resolve and *not* unwrap the state of the promise
+by passing `false` to the first argument of the `wait` function:
+
+```php
+$promise = new Promise();
+$promise->reject('foo');
+// This will not throw an exception. It simply ensures the promise has
+// been resolved.
+$promise->wait(false);
+```
+
+When unwrapping a promise, the resolved value of the promise will be waited
+upon until the unwrapped value is not a promise. This means that if you resolve
+promise A with a promise B and unwrap promise A, the value returned by the
+wait function will be the value delivered to promise B.
+
+**Note**: when you do not unwrap the promise, no value is returned.
+
+
+# Cancellation
+
+You can cancel a promise that has not yet been fulfilled using the `cancel()`
+method of a promise. When creating a promise you can provide an optional
+cancel function that when invoked cancels the action of computing a resolution
+of the promise.
+
+
+# API
+
+
+## Promise
+
+When creating a promise object, you can provide an optional `$waitFn` and
+`$cancelFn`. `$waitFn` is a function that is invoked with no arguments and is
+expected to resolve the promise. `$cancelFn` is a function with no arguments
+that is expected to cancel the computation of a promise. It is invoked when the
+`cancel()` method of a promise is called.
+
+```php
+use GuzzleHttp\Promise\Promise;
+
+$promise = new Promise(
+ function () use (&$promise) {
+ $promise->resolve('waited');
+ },
+ function () {
+ // do something that will cancel the promise computation (e.g., close
+ // a socket, cancel a database query, etc...)
+ }
+);
+
+assert('waited' === $promise->wait());
+```
+
+A promise has the following methods:
+
+- `then(callable $onFulfilled, callable $onRejected) : PromiseInterface`
+
+ Appends fulfillment and rejection handlers to the promise, and returns a new promise resolving to the return value of the called handler.
+
+- `otherwise(callable $onRejected) : PromiseInterface`
+
+ Appends a rejection handler callback to the promise, and returns a new promise resolving to the return value of the callback if it is called, or to its original fulfillment value if the promise is instead fulfilled.
+
+- `wait($unwrap = true) : mixed`
+
+ Synchronously waits on the promise to complete.
+
+ `$unwrap` controls whether or not the value of the promise is returned for a
+ fulfilled promise or if an exception is thrown if the promise is rejected.
+ This is set to `true` by default.
+
+- `cancel()`
+
+ Attempts to cancel the promise if possible. The promise being cancelled and
+ the parent most ancestor that has not yet been resolved will also be
+ cancelled. Any promises waiting on the cancelled promise to resolve will also
+ be cancelled.
+
+- `getState() : string`
+
+ Returns the state of the promise. One of `pending`, `fulfilled`, or
+ `rejected`.
+
+- `resolve($value)`
+
+ Fulfills the promise with the given `$value`.
+
+- `reject($reason)`
+
+ Rejects the promise with the given `$reason`.
+
+
+## FulfilledPromise
+
+A fulfilled promise can be created to represent a promise that has been
+fulfilled.
+
+```php
+use GuzzleHttp\Promise\FulfilledPromise;
+
+$promise = new FulfilledPromise('value');
+
+// Fulfilled callbacks are immediately invoked.
+$promise->then(function ($value) {
+ echo $value;
+});
+```
+
+
+## RejectedPromise
+
+A rejected promise can be created to represent a promise that has been
+rejected.
+
+```php
+use GuzzleHttp\Promise\RejectedPromise;
+
+$promise = new RejectedPromise('Error');
+
+// Rejected callbacks are immediately invoked.
+$promise->then(null, function ($reason) {
+ echo $reason;
+});
+```
+
+
+# Promise interop
+
+This library works with foreign promises that have a `then` method. This means
+you can use Guzzle promises with [React promises](https://github.com/reactphp/promise)
+for example. When a foreign promise is returned inside of a then method
+callback, promise resolution will occur recursively.
+
+```php
+// Create a React promise
+$deferred = new React\Promise\Deferred();
+$reactPromise = $deferred->promise();
+
+// Create a Guzzle promise that is fulfilled with a React promise.
+$guzzlePromise = new \GuzzleHttp\Promise\Promise();
+$guzzlePromise->then(function ($value) use ($reactPromise) {
+ // Do something something with the value...
+ // Return the React promise
+ return $reactPromise;
+});
+```
+
+Please note that wait and cancel chaining is no longer possible when forwarding
+a foreign promise. You will need to wrap a third-party promise with a Guzzle
+promise in order to utilize wait and cancel functions with foreign promises.
+
+
+## Event Loop Integration
+
+In order to keep the stack size constant, Guzzle promises are resolved
+asynchronously using a task queue. When waiting on promises synchronously, the
+task queue will be automatically run to ensure that the blocking promise and
+any forwarded promises are resolved. When using promises asynchronously in an
+event loop, you will need to run the task queue on each tick of the loop. If
+you do not run the task queue, then promises will not be resolved.
+
+You can run the task queue using the `run()` method of the global task queue
+instance.
+
+```php
+// Get the global task queue
+$queue = \GuzzleHttp\Promise\queue();
+$queue->run();
+```
+
+For example, you could use Guzzle promises with React using a periodic timer:
+
+```php
+$loop = React\EventLoop\Factory::create();
+$loop->addPeriodicTimer(0, [$queue, 'run']);
+```
+
+*TODO*: Perhaps adding a `futureTick()` on each tick would be faster?
+
+
+# Implementation notes
+
+
+## Promise resolution and chaining is handled iteratively
+
+By shuffling pending handlers from one owner to another, promises are
+resolved iteratively, allowing for "infinite" then chaining.
+
+```php
+<?php
+require 'vendor/autoload.php';
+
+use GuzzleHttp\Promise\Promise;
+
+$parent = new Promise();
+$p = $parent;
+
+for ($i = 0; $i < 1000; $i++) {
+ $p = $p->then(function ($v) {
+ // The stack size remains constant (a good thing)
+ echo xdebug_get_stack_depth() . ', ';
+ return $v + 1;
+ });
+}
+
+$parent->resolve(0);
+var_dump($p->wait()); // int(1000)
+
+```
+
+When a promise is fulfilled or rejected with a non-promise value, the promise
+then takes ownership of the handlers of each child promise and delivers values
+down the chain without using recursion.
+
+When a promise is resolved with another promise, the original promise transfers
+all of its pending handlers to the new promise. When the new promise is
+eventually resolved, all of the pending handlers are delivered the forwarded
+value.
+
+
+## A promise is the deferred.
+
+Some promise libraries implement promises using a deferred object to represent
+a computation and a promise object to represent the delivery of the result of
+the computation. This is a nice separation of computation and delivery because
+consumers of the promise cannot modify the value that will be eventually
+delivered.
+
+One side effect of being able to implement promise resolution and chaining
+iteratively is that you need to be able for one promise to reach into the state
+of another promise to shuffle around ownership of handlers. In order to achieve
+this without making the handlers of a promise publicly mutable, a promise is
+also the deferred value, allowing promises of the same parent class to reach
+into and modify the private properties of promises of the same type. While this
+does allow consumers of the value to modify the resolution or rejection of the
+deferred, it is a small price to pay for keeping the stack size constant.
+
+```php
+$promise = new Promise();
+$promise->then(function ($value) { echo $value; });
+// The promise is the deferred value, so you can deliver a value to it.
+$promise->resolve('foo');
+// prints "foo"
+```
diff --git a/bin/wiki/vendor/guzzlehttp/promises/composer.json b/bin/wiki/vendor/guzzlehttp/promises/composer.json
new file mode 100644
index 00000000..ec41a61e
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/composer.json
@@ -0,0 +1,34 @@
+{
+ "name": "guzzlehttp/promises",
+ "description": "Guzzle promises library",
+ "keywords": ["promise"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": ["src/functions_include.php"]
+ },
+ "scripts": {
+ "test": "vendor/bin/phpunit",
+ "test-ci": "vendor/bin/phpunit --coverage-text"
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/AggregateException.php b/bin/wiki/vendor/guzzlehttp/promises/src/AggregateException.php
new file mode 100644
index 00000000..6a5690c3
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/AggregateException.php
@@ -0,0 +1,16 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception thrown when too many errors occur in the some() or any() methods.
+ */
+class AggregateException extends RejectionException
+{
+ public function __construct($msg, array $reasons)
+ {
+ parent::__construct(
+ $reasons,
+ sprintf('%s; %d rejected promises', $msg, count($reasons))
+ );
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/CancellationException.php b/bin/wiki/vendor/guzzlehttp/promises/src/CancellationException.php
new file mode 100644
index 00000000..cb360b80
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/CancellationException.php
@@ -0,0 +1,9 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Exception that is set as the reason for a promise that has been cancelled.
+ */
+class CancellationException extends RejectionException
+{
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/Coroutine.php b/bin/wiki/vendor/guzzlehttp/promises/src/Coroutine.php
new file mode 100644
index 00000000..6aa09587
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/Coroutine.php
@@ -0,0 +1,151 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+use Exception;
+use Generator;
+use Throwable;
+
+/**
+ * Creates a promise that is resolved using a generator that yields values or
+ * promises (somewhat similar to C#'s async keyword).
+ *
+ * When called, the coroutine function will start an instance of the generator
+ * and returns a promise that is fulfilled with its final yielded value.
+ *
+ * Control is returned back to the generator when the yielded promise settles.
+ * This can lead to less verbose code when doing lots of sequential async calls
+ * with minimal processing in between.
+ *
+ * use GuzzleHttp\Promise;
+ *
+ * function createPromise($value) {
+ * return new Promise\FulfilledPromise($value);
+ * }
+ *
+ * $promise = Promise\coroutine(function () {
+ * $value = (yield createPromise('a'));
+ * try {
+ * $value = (yield createPromise($value . 'b'));
+ * } catch (\Exception $e) {
+ * // The promise was rejected.
+ * }
+ * yield $value . 'c';
+ * });
+ *
+ * // Outputs "abc"
+ * $promise->then(function ($v) { echo $v; });
+ *
+ * @param callable $generatorFn Generator function to wrap into a promise.
+ *
+ * @return Promise
+ * @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
+ */
+final class Coroutine implements PromiseInterface
+{
+ /**
+ * @var PromiseInterface|null
+ */
+ private $currentPromise;
+
+ /**
+ * @var Generator
+ */
+ private $generator;
+
+ /**
+ * @var Promise
+ */
+ private $result;
+
+ public function __construct(callable $generatorFn)
+ {
+ $this->generator = $generatorFn();
+ $this->result = new Promise(function () {
+ while (isset($this->currentPromise)) {
+ $this->currentPromise->wait();
+ }
+ });
+ $this->nextCoroutine($this->generator->current());
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ return $this->result->then($onFulfilled, $onRejected);
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->result->otherwise($onRejected);
+ }
+
+ public function wait($unwrap = true)
+ {
+ return $this->result->wait($unwrap);
+ }
+
+ public function getState()
+ {
+ return $this->result->getState();
+ }
+
+ public function resolve($value)
+ {
+ $this->result->resolve($value);
+ }
+
+ public function reject($reason)
+ {
+ $this->result->reject($reason);
+ }
+
+ public function cancel()
+ {
+ $this->currentPromise->cancel();
+ $this->result->cancel();
+ }
+
+ private function nextCoroutine($yielded)
+ {
+ $this->currentPromise = promise_for($yielded)
+ ->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
+ }
+
+ /**
+ * @internal
+ */
+ public function _handleSuccess($value)
+ {
+ unset($this->currentPromise);
+ try {
+ $next = $this->generator->send($value);
+ if ($this->generator->valid()) {
+ $this->nextCoroutine($next);
+ } else {
+ $this->result->resolve($value);
+ }
+ } catch (Exception $exception) {
+ $this->result->reject($exception);
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+
+ /**
+ * @internal
+ */
+ public function _handleFailure($reason)
+ {
+ unset($this->currentPromise);
+ try {
+ $nextYield = $this->generator->throw(exception_for($reason));
+ // The throw was caught, so keep iterating on the coroutine
+ $this->nextCoroutine($nextYield);
+ } catch (Exception $exception) {
+ $this->result->reject($exception);
+ } catch (Throwable $throwable) {
+ $this->result->reject($throwable);
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/EachPromise.php b/bin/wiki/vendor/guzzlehttp/promises/src/EachPromise.php
new file mode 100644
index 00000000..d0ddf603
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/EachPromise.php
@@ -0,0 +1,229 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Represents a promise that iterates over many promises and invokes
+ * side-effect functions in the process.
+ */
+class EachPromise implements PromisorInterface
+{
+ private $pending = [];
+
+ /** @var \Iterator */
+ private $iterable;
+
+ /** @var callable|int */
+ private $concurrency;
+
+ /** @var callable */
+ private $onFulfilled;
+
+ /** @var callable */
+ private $onRejected;
+
+ /** @var Promise */
+ private $aggregate;
+
+ /** @var bool */
+ private $mutex;
+
+ /**
+ * Configuration hash can include the following key value pairs:
+ *
+ * - fulfilled: (callable) Invoked when a promise fulfills. The function
+ * is invoked with three arguments: the fulfillment value, the index
+ * position from the iterable list of the promise, and the aggregate
+ * promise that manages all of the promises. The aggregate promise may
+ * be resolved from within the callback to short-circuit the promise.
+ * - rejected: (callable) Invoked when a promise is rejected. The
+ * function is invoked with three arguments: the rejection reason, the
+ * index position from the iterable list of the promise, and the
+ * aggregate promise that manages all of the promises. The aggregate
+ * promise may be resolved from within the callback to short-circuit
+ * the promise.
+ * - concurrency: (integer) Pass this configuration option to limit the
+ * allowed number of outstanding concurrently executing promises,
+ * creating a capped pool of promises. There is no limit by default.
+ *
+ * @param mixed $iterable Promises or values to iterate.
+ * @param array $config Configuration options
+ */
+ public function __construct($iterable, array $config = [])
+ {
+ $this->iterable = iter_for($iterable);
+
+ if (isset($config['concurrency'])) {
+ $this->concurrency = $config['concurrency'];
+ }
+
+ if (isset($config['fulfilled'])) {
+ $this->onFulfilled = $config['fulfilled'];
+ }
+
+ if (isset($config['rejected'])) {
+ $this->onRejected = $config['rejected'];
+ }
+ }
+
+ public function promise()
+ {
+ if ($this->aggregate) {
+ return $this->aggregate;
+ }
+
+ try {
+ $this->createPromise();
+ $this->iterable->rewind();
+ $this->refillPending();
+ } catch (\Throwable $e) {
+ $this->aggregate->reject($e);
+ } catch (\Exception $e) {
+ $this->aggregate->reject($e);
+ }
+
+ return $this->aggregate;
+ }
+
+ private function createPromise()
+ {
+ $this->mutex = false;
+ $this->aggregate = new Promise(function () {
+ reset($this->pending);
+ if (empty($this->pending) && !$this->iterable->valid()) {
+ $this->aggregate->resolve(null);
+ return;
+ }
+
+ // Consume a potentially fluctuating list of promises while
+ // ensuring that indexes are maintained (precluding array_shift).
+ while ($promise = current($this->pending)) {
+ next($this->pending);
+ $promise->wait();
+ if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
+ return;
+ }
+ }
+ });
+
+ // Clear the references when the promise is resolved.
+ $clearFn = function () {
+ $this->iterable = $this->concurrency = $this->pending = null;
+ $this->onFulfilled = $this->onRejected = null;
+ };
+
+ $this->aggregate->then($clearFn, $clearFn);
+ }
+
+ private function refillPending()
+ {
+ if (!$this->concurrency) {
+ // Add all pending promises.
+ while ($this->addPending() && $this->advanceIterator());
+ return;
+ }
+
+ // Add only up to N pending promises.
+ $concurrency = is_callable($this->concurrency)
+ ? call_user_func($this->concurrency, count($this->pending))
+ : $this->concurrency;
+ $concurrency = max($concurrency - count($this->pending), 0);
+ // Concurrency may be set to 0 to disallow new promises.
+ if (!$concurrency) {
+ return;
+ }
+ // Add the first pending promise.
+ $this->addPending();
+ // Note this is special handling for concurrency=1 so that we do
+ // not advance the iterator after adding the first promise. This
+ // helps work around issues with generators that might not have the
+ // next value to yield until promise callbacks are called.
+ while (--$concurrency
+ && $this->advanceIterator()
+ && $this->addPending());
+ }
+
+ private function addPending()
+ {
+ if (!$this->iterable || !$this->iterable->valid()) {
+ return false;
+ }
+
+ $promise = promise_for($this->iterable->current());
+ $idx = $this->iterable->key();
+
+ $this->pending[$idx] = $promise->then(
+ function ($value) use ($idx) {
+ if ($this->onFulfilled) {
+ call_user_func(
+ $this->onFulfilled, $value, $idx, $this->aggregate
+ );
+ }
+ $this->step($idx);
+ },
+ function ($reason) use ($idx) {
+ if ($this->onRejected) {
+ call_user_func(
+ $this->onRejected, $reason, $idx, $this->aggregate
+ );
+ }
+ $this->step($idx);
+ }
+ );
+
+ return true;
+ }
+
+ private function advanceIterator()
+ {
+ // Place a lock on the iterator so that we ensure to not recurse,
+ // preventing fatal generator errors.
+ if ($this->mutex) {
+ return false;
+ }
+
+ $this->mutex = true;
+
+ try {
+ $this->iterable->next();
+ $this->mutex = false;
+ return true;
+ } catch (\Throwable $e) {
+ $this->aggregate->reject($e);
+ $this->mutex = false;
+ return false;
+ } catch (\Exception $e) {
+ $this->aggregate->reject($e);
+ $this->mutex = false;
+ return false;
+ }
+ }
+
+ private function step($idx)
+ {
+ // If the promise was already resolved, then ignore this step.
+ if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
+ return;
+ }
+
+ unset($this->pending[$idx]);
+
+ // Only refill pending promises if we are not locked, preventing the
+ // EachPromise to recursively invoke the provided iterator, which
+ // cause a fatal error: "Cannot resume an already running generator"
+ if ($this->advanceIterator() && !$this->checkIfFinished()) {
+ // Add more pending promises if possible.
+ $this->refillPending();
+ }
+ }
+
+ private function checkIfFinished()
+ {
+ if (!$this->pending && !$this->iterable->valid()) {
+ // Resolve the promise if there's nothing left to do.
+ $this->aggregate->resolve(null);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/FulfilledPromise.php b/bin/wiki/vendor/guzzlehttp/promises/src/FulfilledPromise.php
new file mode 100644
index 00000000..dbbeeb9f
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/FulfilledPromise.php
@@ -0,0 +1,82 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been fulfilled.
+ *
+ * Thenning off of this promise will invoke the onFulfilled callback
+ * immediately and ignore other callbacks.
+ */
+class FulfilledPromise implements PromiseInterface
+{
+ private $value;
+
+ public function __construct($value)
+ {
+ if (method_exists($value, 'then')) {
+ throw new \InvalidArgumentException(
+ 'You cannot create a FulfilledPromise with a promise.');
+ }
+
+ $this->value = $value;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ // Return itself if there is no onFulfilled function.
+ if (!$onFulfilled) {
+ return $this;
+ }
+
+ $queue = queue();
+ $p = new Promise([$queue, 'run']);
+ $value = $this->value;
+ $queue->add(static function () use ($p, $value, $onFulfilled) {
+ if ($p->getState() === self::PENDING) {
+ try {
+ $p->resolve($onFulfilled($value));
+ } catch (\Throwable $e) {
+ $p->reject($e);
+ } catch (\Exception $e) {
+ $p->reject($e);
+ }
+ }
+ });
+
+ return $p;
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait($unwrap = true, $defaultDelivery = null)
+ {
+ return $unwrap ? $this->value : null;
+ }
+
+ public function getState()
+ {
+ return self::FULFILLED;
+ }
+
+ public function resolve($value)
+ {
+ if ($value !== $this->value) {
+ throw new \LogicException("Cannot resolve a fulfilled promise");
+ }
+ }
+
+ public function reject($reason)
+ {
+ throw new \LogicException("Cannot reject a fulfilled promise");
+ }
+
+ public function cancel()
+ {
+ // pass
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/Promise.php b/bin/wiki/vendor/guzzlehttp/promises/src/Promise.php
new file mode 100644
index 00000000..844ada07
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/Promise.php
@@ -0,0 +1,280 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Promises/A+ implementation that avoids recursion when possible.
+ *
+ * @link https://promisesaplus.com/
+ */
+class Promise implements PromiseInterface
+{
+ private $state = self::PENDING;
+ private $result;
+ private $cancelFn;
+ private $waitFn;
+ private $waitList;
+ private $handlers = [];
+
+ /**
+ * @param callable $waitFn Fn that when invoked resolves the promise.
+ * @param callable $cancelFn Fn that when invoked cancels the promise.
+ */
+ public function __construct(
+ callable $waitFn = null,
+ callable $cancelFn = null
+ ) {
+ $this->waitFn = $waitFn;
+ $this->cancelFn = $cancelFn;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ if ($this->state === self::PENDING) {
+ $p = new Promise(null, [$this, 'cancel']);
+ $this->handlers[] = [$p, $onFulfilled, $onRejected];
+ $p->waitList = $this->waitList;
+ $p->waitList[] = $this;
+ return $p;
+ }
+
+ // Return a fulfilled promise and immediately invoke any callbacks.
+ if ($this->state === self::FULFILLED) {
+ return $onFulfilled
+ ? promise_for($this->result)->then($onFulfilled)
+ : promise_for($this->result);
+ }
+
+ // It's either cancelled or rejected, so return a rejected promise
+ // and immediately invoke any callbacks.
+ $rejection = rejection_for($this->result);
+ return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait($unwrap = true)
+ {
+ $this->waitIfPending();
+
+ $inner = $this->result instanceof PromiseInterface
+ ? $this->result->wait($unwrap)
+ : $this->result;
+
+ if ($unwrap) {
+ if ($this->result instanceof PromiseInterface
+ || $this->state === self::FULFILLED
+ ) {
+ return $inner;
+ } else {
+ // It's rejected so "unwrap" and throw an exception.
+ throw exception_for($inner);
+ }
+ }
+ }
+
+ public function getState()
+ {
+ return $this->state;
+ }
+
+ public function cancel()
+ {
+ if ($this->state !== self::PENDING) {
+ return;
+ }
+
+ $this->waitFn = $this->waitList = null;
+
+ if ($this->cancelFn) {
+ $fn = $this->cancelFn;
+ $this->cancelFn = null;
+ try {
+ $fn();
+ } catch (\Throwable $e) {
+ $this->reject($e);
+ } catch (\Exception $e) {
+ $this->reject($e);
+ }
+ }
+
+ // Reject the promise only if it wasn't rejected in a then callback.
+ if ($this->state === self::PENDING) {
+ $this->reject(new CancellationException('Promise has been cancelled'));
+ }
+ }
+
+ public function resolve($value)
+ {
+ $this->settle(self::FULFILLED, $value);
+ }
+
+ public function reject($reason)
+ {
+ $this->settle(self::REJECTED, $reason);
+ }
+
+ private function settle($state, $value)
+ {
+ if ($this->state !== self::PENDING) {
+ // Ignore calls with the same resolution.
+ if ($state === $this->state && $value === $this->result) {
+ return;
+ }
+ throw $this->state === $state
+ ? new \LogicException("The promise is already {$state}.")
+ : new \LogicException("Cannot change a {$this->state} promise to {$state}");
+ }
+
+ if ($value === $this) {
+ throw new \LogicException('Cannot fulfill or reject a promise with itself');
+ }
+
+ // Clear out the state of the promise but stash the handlers.
+ $this->state = $state;
+ $this->result = $value;
+ $handlers = $this->handlers;
+ $this->handlers = null;
+ $this->waitList = $this->waitFn = null;
+ $this->cancelFn = null;
+
+ if (!$handlers) {
+ return;
+ }
+
+ // If the value was not a settled promise or a thenable, then resolve
+ // it in the task queue using the correct ID.
+ if (!method_exists($value, 'then')) {
+ $id = $state === self::FULFILLED ? 1 : 2;
+ // It's a success, so resolve the handlers in the queue.
+ queue()->add(static function () use ($id, $value, $handlers) {
+ foreach ($handlers as $handler) {
+ self::callHandler($id, $value, $handler);
+ }
+ });
+ } elseif ($value instanceof Promise
+ && $value->getState() === self::PENDING
+ ) {
+ // We can just merge our handlers onto the next promise.
+ $value->handlers = array_merge($value->handlers, $handlers);
+ } else {
+ // Resolve the handlers when the forwarded promise is resolved.
+ $value->then(
+ static function ($value) use ($handlers) {
+ foreach ($handlers as $handler) {
+ self::callHandler(1, $value, $handler);
+ }
+ },
+ static function ($reason) use ($handlers) {
+ foreach ($handlers as $handler) {
+ self::callHandler(2, $reason, $handler);
+ }
+ }
+ );
+ }
+ }
+
+ /**
+ * Call a stack of handlers using a specific callback index and value.
+ *
+ * @param int $index 1 (resolve) or 2 (reject).
+ * @param mixed $value Value to pass to the callback.
+ * @param array $handler Array of handler data (promise and callbacks).
+ *
+ * @return array Returns the next group to resolve.
+ */
+ private static function callHandler($index, $value, array $handler)
+ {
+ /** @var PromiseInterface $promise */
+ $promise = $handler[0];
+
+ // The promise may have been cancelled or resolved before placing
+ // this thunk in the queue.
+ if ($promise->getState() !== self::PENDING) {
+ return;
+ }
+
+ try {
+ if (isset($handler[$index])) {
+ $promise->resolve($handler[$index]($value));
+ } elseif ($index === 1) {
+ // Forward resolution values as-is.
+ $promise->resolve($value);
+ } else {
+ // Forward rejections down the chain.
+ $promise->reject($value);
+ }
+ } catch (\Throwable $reason) {
+ $promise->reject($reason);
+ } catch (\Exception $reason) {
+ $promise->reject($reason);
+ }
+ }
+
+ private function waitIfPending()
+ {
+ if ($this->state !== self::PENDING) {
+ return;
+ } elseif ($this->waitFn) {
+ $this->invokeWaitFn();
+ } elseif ($this->waitList) {
+ $this->invokeWaitList();
+ } else {
+ // If there's not wait function, then reject the promise.
+ $this->reject('Cannot wait on a promise that has '
+ . 'no internal wait function. You must provide a wait '
+ . 'function when constructing the promise to be able to '
+ . 'wait on a promise.');
+ }
+
+ queue()->run();
+
+ if ($this->state === self::PENDING) {
+ $this->reject('Invoking the wait callback did not resolve the promise');
+ }
+ }
+
+ private function invokeWaitFn()
+ {
+ try {
+ $wfn = $this->waitFn;
+ $this->waitFn = null;
+ $wfn(true);
+ } catch (\Exception $reason) {
+ if ($this->state === self::PENDING) {
+ // The promise has not been resolved yet, so reject the promise
+ // with the exception.
+ $this->reject($reason);
+ } else {
+ // The promise was already resolved, so there's a problem in
+ // the application.
+ throw $reason;
+ }
+ }
+ }
+
+ private function invokeWaitList()
+ {
+ $waitList = $this->waitList;
+ $this->waitList = null;
+
+ foreach ($waitList as $result) {
+ while (true) {
+ $result->waitIfPending();
+
+ if ($result->result instanceof Promise) {
+ $result = $result->result;
+ } else {
+ if ($result->result instanceof PromiseInterface) {
+ $result->result->wait(false);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/PromiseInterface.php b/bin/wiki/vendor/guzzlehttp/promises/src/PromiseInterface.php
new file mode 100644
index 00000000..8f5f4b99
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/PromiseInterface.php
@@ -0,0 +1,93 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise represents the eventual result of an asynchronous operation.
+ *
+ * The primary way of interacting with a promise is through its then method,
+ * which registers callbacks to receive either a promise’s eventual value or
+ * the reason why the promise cannot be fulfilled.
+ *
+ * @link https://promisesaplus.com/
+ */
+interface PromiseInterface
+{
+ const PENDING = 'pending';
+ const FULFILLED = 'fulfilled';
+ const REJECTED = 'rejected';
+
+ /**
+ * Appends fulfillment and rejection handlers to the promise, and returns
+ * a new promise resolving to the return value of the called handler.
+ *
+ * @param callable $onFulfilled Invoked when the promise fulfills.
+ * @param callable $onRejected Invoked when the promise is rejected.
+ *
+ * @return PromiseInterface
+ */
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ );
+
+ /**
+ * Appends a rejection handler callback to the promise, and returns a new
+ * promise resolving to the return value of the callback if it is called,
+ * or to its original fulfillment value if the promise is instead
+ * fulfilled.
+ *
+ * @param callable $onRejected Invoked when the promise is rejected.
+ *
+ * @return PromiseInterface
+ */
+ public function otherwise(callable $onRejected);
+
+ /**
+ * Get the state of the promise ("pending", "rejected", or "fulfilled").
+ *
+ * The three states can be checked against the constants defined on
+ * PromiseInterface: PENDING, FULFILLED, and REJECTED.
+ *
+ * @return string
+ */
+ public function getState();
+
+ /**
+ * Resolve the promise with the given value.
+ *
+ * @param mixed $value
+ * @throws \RuntimeException if the promise is already resolved.
+ */
+ public function resolve($value);
+
+ /**
+ * Reject the promise with the given reason.
+ *
+ * @param mixed $reason
+ * @throws \RuntimeException if the promise is already resolved.
+ */
+ public function reject($reason);
+
+ /**
+ * Cancels the promise if possible.
+ *
+ * @link https://github.com/promises-aplus/cancellation-spec/issues/7
+ */
+ public function cancel();
+
+ /**
+ * Waits until the promise completes if possible.
+ *
+ * Pass $unwrap as true to unwrap the result of the promise, either
+ * returning the resolved value or throwing the rejected exception.
+ *
+ * If the promise cannot be waited on, then the promise will be rejected.
+ *
+ * @param bool $unwrap
+ *
+ * @return mixed
+ * @throws \LogicException if the promise has no wait function or if the
+ * promise does not settle after waiting.
+ */
+ public function wait($unwrap = true);
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/PromisorInterface.php b/bin/wiki/vendor/guzzlehttp/promises/src/PromisorInterface.php
new file mode 100644
index 00000000..b07fe32b
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/PromisorInterface.php
@@ -0,0 +1,15 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Interface used with classes that return a promise.
+ */
+interface PromisorInterface
+{
+ /**
+ * Returns a promise.
+ *
+ * @return PromiseInterface
+ */
+ public function promise();
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/RejectedPromise.php b/bin/wiki/vendor/guzzlehttp/promises/src/RejectedPromise.php
new file mode 100644
index 00000000..2bc6508e
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/RejectedPromise.php
@@ -0,0 +1,87 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A promise that has been rejected.
+ *
+ * Thenning off of this promise will invoke the onRejected callback
+ * immediately and ignore other callbacks.
+ */
+class RejectedPromise implements PromiseInterface
+{
+ private $reason;
+
+ public function __construct($reason)
+ {
+ if (method_exists($reason, 'then')) {
+ throw new \InvalidArgumentException(
+ 'You cannot create a RejectedPromise with a promise.');
+ }
+
+ $this->reason = $reason;
+ }
+
+ public function then(
+ callable $onFulfilled = null,
+ callable $onRejected = null
+ ) {
+ // If there's no onRejected callback then just return self.
+ if (!$onRejected) {
+ return $this;
+ }
+
+ $queue = queue();
+ $reason = $this->reason;
+ $p = new Promise([$queue, 'run']);
+ $queue->add(static function () use ($p, $reason, $onRejected) {
+ if ($p->getState() === self::PENDING) {
+ try {
+ // Return a resolved promise if onRejected does not throw.
+ $p->resolve($onRejected($reason));
+ } catch (\Throwable $e) {
+ // onRejected threw, so return a rejected promise.
+ $p->reject($e);
+ } catch (\Exception $e) {
+ // onRejected threw, so return a rejected promise.
+ $p->reject($e);
+ }
+ }
+ });
+
+ return $p;
+ }
+
+ public function otherwise(callable $onRejected)
+ {
+ return $this->then(null, $onRejected);
+ }
+
+ public function wait($unwrap = true, $defaultDelivery = null)
+ {
+ if ($unwrap) {
+ throw exception_for($this->reason);
+ }
+ }
+
+ public function getState()
+ {
+ return self::REJECTED;
+ }
+
+ public function resolve($value)
+ {
+ throw new \LogicException("Cannot resolve a rejected promise");
+ }
+
+ public function reject($reason)
+ {
+ if ($reason !== $this->reason) {
+ throw new \LogicException("Cannot reject a rejected promise");
+ }
+ }
+
+ public function cancel()
+ {
+ // pass
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/RejectionException.php b/bin/wiki/vendor/guzzlehttp/promises/src/RejectionException.php
new file mode 100644
index 00000000..07c1136d
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/RejectionException.php
@@ -0,0 +1,47 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A special exception that is thrown when waiting on a rejected promise.
+ *
+ * The reason value is available via the getReason() method.
+ */
+class RejectionException extends \RuntimeException
+{
+ /** @var mixed Rejection reason. */
+ private $reason;
+
+ /**
+ * @param mixed $reason Rejection reason.
+ * @param string $description Optional description
+ */
+ public function __construct($reason, $description = null)
+ {
+ $this->reason = $reason;
+
+ $message = 'The promise was rejected';
+
+ if ($description) {
+ $message .= ' with reason: ' . $description;
+ } elseif (is_string($reason)
+ || (is_object($reason) && method_exists($reason, '__toString'))
+ ) {
+ $message .= ' with reason: ' . $this->reason;
+ } elseif ($reason instanceof \JsonSerializable) {
+ $message .= ' with reason: '
+ . json_encode($this->reason, JSON_PRETTY_PRINT);
+ }
+
+ parent::__construct($message);
+ }
+
+ /**
+ * Returns the rejection reason.
+ *
+ * @return mixed
+ */
+ public function getReason()
+ {
+ return $this->reason;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/TaskQueue.php b/bin/wiki/vendor/guzzlehttp/promises/src/TaskQueue.php
new file mode 100644
index 00000000..6e8a2a08
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/TaskQueue.php
@@ -0,0 +1,66 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * A task queue that executes tasks in a FIFO order.
+ *
+ * This task queue class is used to settle promises asynchronously and
+ * maintains a constant stack size. You can use the task queue asynchronously
+ * by calling the `run()` function of the global task queue in an event loop.
+ *
+ * GuzzleHttp\Promise\queue()->run();
+ */
+class TaskQueue implements TaskQueueInterface
+{
+ private $enableShutdown = true;
+ private $queue = [];
+
+ public function __construct($withShutdown = true)
+ {
+ if ($withShutdown) {
+ register_shutdown_function(function () {
+ if ($this->enableShutdown) {
+ // Only run the tasks if an E_ERROR didn't occur.
+ $err = error_get_last();
+ if (!$err || ($err['type'] ^ E_ERROR)) {
+ $this->run();
+ }
+ }
+ });
+ }
+ }
+
+ public function isEmpty()
+ {
+ return !$this->queue;
+ }
+
+ public function add(callable $task)
+ {
+ $this->queue[] = $task;
+ }
+
+ public function run()
+ {
+ /** @var callable $task */
+ while ($task = array_shift($this->queue)) {
+ $task();
+ }
+ }
+
+ /**
+ * The task queue will be run and exhausted by default when the process
+ * exits IFF the exit is not the result of a PHP E_ERROR error.
+ *
+ * You can disable running the automatic shutdown of the queue by calling
+ * this function. If you disable the task queue shutdown process, then you
+ * MUST either run the task queue (as a result of running your event loop
+ * or manually using the run() method) or wait on each outstanding promise.
+ *
+ * Note: This shutdown will occur before any destructors are triggered.
+ */
+ public function disableShutdown()
+ {
+ $this->enableShutdown = false;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/TaskQueueInterface.php b/bin/wiki/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
new file mode 100644
index 00000000..ac8306e1
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/TaskQueueInterface.php
@@ -0,0 +1,25 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+interface TaskQueueInterface
+{
+ /**
+ * Returns true if the queue is empty.
+ *
+ * @return bool
+ */
+ public function isEmpty();
+
+ /**
+ * Adds a task to the queue that will be executed the next time run is
+ * called.
+ *
+ * @param callable $task
+ */
+ public function add(callable $task);
+
+ /**
+ * Execute all of the pending task in the queue.
+ */
+ public function run();
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/functions.php b/bin/wiki/vendor/guzzlehttp/promises/src/functions.php
new file mode 100644
index 00000000..4e27709a
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/functions.php
@@ -0,0 +1,457 @@
+<?php
+namespace GuzzleHttp\Promise;
+
+/**
+ * Get the global task queue used for promise resolution.
+ *
+ * This task queue MUST be run in an event loop in order for promises to be
+ * settled asynchronously. It will be automatically run when synchronously
+ * waiting on a promise.
+ *
+ * <code>
+ * while ($eventLoop->isRunning()) {
+ * GuzzleHttp\Promise\queue()->run();
+ * }
+ * </code>
+ *
+ * @param TaskQueueInterface $assign Optionally specify a new queue instance.
+ *
+ * @return TaskQueueInterface
+ */
+function queue(TaskQueueInterface $assign = null)
+{
+ static $queue;
+
+ if ($assign) {
+ $queue = $assign;
+ } elseif (!$queue) {
+ $queue = new TaskQueue();
+ }
+
+ return $queue;
+}
+
+/**
+ * Adds a function to run in the task queue when it is next `run()` and returns
+ * a promise that is fulfilled or rejected with the result.
+ *
+ * @param callable $task Task function to run.
+ *
+ * @return PromiseInterface
+ */
+function task(callable $task)
+{
+ $queue = queue();
+ $promise = new Promise([$queue, 'run']);
+ $queue->add(function () use ($task, $promise) {
+ try {
+ $promise->resolve($task());
+ } catch (\Throwable $e) {
+ $promise->reject($e);
+ } catch (\Exception $e) {
+ $promise->reject($e);
+ }
+ });
+
+ return $promise;
+}
+
+/**
+ * Creates a promise for a value if the value is not a promise.
+ *
+ * @param mixed $value Promise or value.
+ *
+ * @return PromiseInterface
+ */
+function promise_for($value)
+{
+ if ($value instanceof PromiseInterface) {
+ return $value;
+ }
+
+ // Return a Guzzle promise that shadows the given promise.
+ if (method_exists($value, 'then')) {
+ $wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
+ $cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
+ $promise = new Promise($wfn, $cfn);
+ $value->then([$promise, 'resolve'], [$promise, 'reject']);
+ return $promise;
+ }
+
+ return new FulfilledPromise($value);
+}
+
+/**
+ * Creates a rejected promise for a reason if the reason is not a promise. If
+ * the provided reason is a promise, then it is returned as-is.
+ *
+ * @param mixed $reason Promise or reason.
+ *
+ * @return PromiseInterface
+ */
+function rejection_for($reason)
+{
+ if ($reason instanceof PromiseInterface) {
+ return $reason;
+ }
+
+ return new RejectedPromise($reason);
+}
+
+/**
+ * Create an exception for a rejected promise value.
+ *
+ * @param mixed $reason
+ *
+ * @return \Exception|\Throwable
+ */
+function exception_for($reason)
+{
+ return $reason instanceof \Exception || $reason instanceof \Throwable
+ ? $reason
+ : new RejectionException($reason);
+}
+
+/**
+ * Returns an iterator for the given value.
+ *
+ * @param mixed $value
+ *
+ * @return \Iterator
+ */
+function iter_for($value)
+{
+ if ($value instanceof \Iterator) {
+ return $value;
+ } elseif (is_array($value)) {
+ return new \ArrayIterator($value);
+ } else {
+ return new \ArrayIterator([$value]);
+ }
+}
+
+/**
+ * Synchronously waits on a promise to resolve and returns an inspection state
+ * array.
+ *
+ * Returns a state associative array containing a "state" key mapping to a
+ * valid promise state. If the state of the promise is "fulfilled", the array
+ * will contain a "value" key mapping to the fulfilled value of the promise. If
+ * the promise is rejected, the array will contain a "reason" key mapping to
+ * the rejection reason of the promise.
+ *
+ * @param PromiseInterface $promise Promise or value.
+ *
+ * @return array
+ */
+function inspect(PromiseInterface $promise)
+{
+ try {
+ return [
+ 'state' => PromiseInterface::FULFILLED,
+ 'value' => $promise->wait()
+ ];
+ } catch (RejectionException $e) {
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
+ } catch (\Throwable $e) {
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+ } catch (\Exception $e) {
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
+ }
+}
+
+/**
+ * Waits on all of the provided promises, but does not unwrap rejected promises
+ * as thrown exception.
+ *
+ * Returns an array of inspection state arrays.
+ *
+ * @param PromiseInterface[] $promises Traversable of promises to wait upon.
+ *
+ * @return array
+ * @see GuzzleHttp\Promise\inspect for the inspection state array format.
+ */
+function inspect_all($promises)
+{
+ $results = [];
+ foreach ($promises as $key => $promise) {
+ $results[$key] = inspect($promise);
+ }
+
+ return $results;
+}
+
+/**
+ * Waits on all of the provided promises and returns the fulfilled values.
+ *
+ * Returns an array that contains the value of each promise (in the same order
+ * the promises were provided). An exception is thrown if any of the promises
+ * are rejected.
+ *
+ * @param mixed $promises Iterable of PromiseInterface objects to wait on.
+ *
+ * @return array
+ * @throws \Exception on error
+ * @throws \Throwable on error in PHP >=7
+ */
+function unwrap($promises)
+{
+ $results = [];
+ foreach ($promises as $key => $promise) {
+ $results[$key] = $promise->wait();
+ }
+
+ return $results;
+}
+
+/**
+ * Given an array of promises, return a promise that is fulfilled when all the
+ * items in the array are fulfilled.
+ *
+ * The promise's fulfillment value is an array with fulfillment values at
+ * respective positions to the original array. If any promise in the array
+ * rejects, the returned promise is rejected with the rejection reason.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function all($promises)
+{
+ $results = [];
+ return each(
+ $promises,
+ function ($value, $idx) use (&$results) {
+ $results[$idx] = $value;
+ },
+ function ($reason, $idx, Promise $aggregate) {
+ $aggregate->reject($reason);
+ }
+ )->then(function () use (&$results) {
+ ksort($results);
+ return $results;
+ });
+}
+
+/**
+ * Initiate a competitive race between multiple promises or values (values will
+ * become immediately fulfilled promises).
+ *
+ * When count amount of promises have been fulfilled, the returned promise is
+ * fulfilled with an array that contains the fulfillment values of the winners
+ * in order of resolution.
+ *
+ * This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException}
+ * if the number of fulfilled promises is less than the desired $count.
+ *
+ * @param int $count Total number of promises.
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function some($count, $promises)
+{
+ $results = [];
+ $rejections = [];
+
+ return each(
+ $promises,
+ function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
+ if ($p->getState() !== PromiseInterface::PENDING) {
+ return;
+ }
+ $results[$idx] = $value;
+ if (count($results) >= $count) {
+ $p->resolve(null);
+ }
+ },
+ function ($reason) use (&$rejections) {
+ $rejections[] = $reason;
+ }
+ )->then(
+ function () use (&$results, &$rejections, $count) {
+ if (count($results) !== $count) {
+ throw new AggregateException(
+ 'Not enough promises to fulfill count',
+ $rejections
+ );
+ }
+ ksort($results);
+ return array_values($results);
+ }
+ );
+}
+
+/**
+ * Like some(), with 1 as count. However, if the promise fulfills, the
+ * fulfillment value is not an array of 1 but the value directly.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ */
+function any($promises)
+{
+ return some(1, $promises)->then(function ($values) { return $values[0]; });
+}
+
+/**
+ * Returns a promise that is fulfilled when all of the provided promises have
+ * been fulfilled or rejected.
+ *
+ * The returned promise is fulfilled with an array of inspection state arrays.
+ *
+ * @param mixed $promises Promises or values.
+ *
+ * @return PromiseInterface
+ * @see GuzzleHttp\Promise\inspect for the inspection state array format.
+ */
+function settle($promises)
+{
+ $results = [];
+
+ return each(
+ $promises,
+ function ($value, $idx) use (&$results) {
+ $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
+ },
+ function ($reason, $idx) use (&$results) {
+ $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
+ }
+ )->then(function () use (&$results) {
+ ksort($results);
+ return $results;
+ });
+}
+
+/**
+ * Given an iterator that yields promises or values, returns a promise that is
+ * fulfilled with a null value when the iterator has been consumed or the
+ * aggregate promise has been fulfilled or rejected.
+ *
+ * $onFulfilled is a function that accepts the fulfilled value, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate promise if needed.
+ *
+ * $onRejected is a function that accepts the rejection reason, iterator
+ * index, and the aggregate promise. The callback can invoke any necessary side
+ * effects and choose to resolve or reject the aggregate promise if needed.
+ *
+ * @param mixed $iterable Iterator or array to iterate over.
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ *
+ * @return PromiseInterface
+ */
+function each(
+ $iterable,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+) {
+ return (new EachPromise($iterable, [
+ 'fulfilled' => $onFulfilled,
+ 'rejected' => $onRejected
+ ]))->promise();
+}
+
+/**
+ * Like each, but only allows a certain number of outstanding promises at any
+ * given time.
+ *
+ * $concurrency may be an integer or a function that accepts the number of
+ * pending promises and returns a numeric concurrency limit value to allow for
+ * dynamic a concurrency size.
+ *
+ * @param mixed $iterable
+ * @param int|callable $concurrency
+ * @param callable $onFulfilled
+ * @param callable $onRejected
+ *
+ * @return PromiseInterface
+ */
+function each_limit(
+ $iterable,
+ $concurrency,
+ callable $onFulfilled = null,
+ callable $onRejected = null
+) {
+ return (new EachPromise($iterable, [
+ 'fulfilled' => $onFulfilled,
+ 'rejected' => $onRejected,
+ 'concurrency' => $concurrency
+ ]))->promise();
+}
+
+/**
+ * Like each_limit, but ensures that no promise in the given $iterable argument
+ * is rejected. If any promise is rejected, then the aggregate promise is
+ * rejected with the encountered rejection.
+ *
+ * @param mixed $iterable
+ * @param int|callable $concurrency
+ * @param callable $onFulfilled
+ *
+ * @return PromiseInterface
+ */
+function each_limit_all(
+ $iterable,
+ $concurrency,
+ callable $onFulfilled = null
+) {
+ return each_limit(
+ $iterable,
+ $concurrency,
+ $onFulfilled,
+ function ($reason, $idx, PromiseInterface $aggregate) {
+ $aggregate->reject($reason);
+ }
+ );
+}
+
+/**
+ * Returns true if a promise is fulfilled.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_fulfilled(PromiseInterface $promise)
+{
+ return $promise->getState() === PromiseInterface::FULFILLED;
+}
+
+/**
+ * Returns true if a promise is rejected.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_rejected(PromiseInterface $promise)
+{
+ return $promise->getState() === PromiseInterface::REJECTED;
+}
+
+/**
+ * Returns true if a promise is fulfilled or rejected.
+ *
+ * @param PromiseInterface $promise
+ *
+ * @return bool
+ */
+function is_settled(PromiseInterface $promise)
+{
+ return $promise->getState() !== PromiseInterface::PENDING;
+}
+
+/**
+ * @see Coroutine
+ *
+ * @param callable $generatorFn
+ *
+ * @return PromiseInterface
+ */
+function coroutine(callable $generatorFn)
+{
+ return new Coroutine($generatorFn);
+}
diff --git a/bin/wiki/vendor/guzzlehttp/promises/src/functions_include.php b/bin/wiki/vendor/guzzlehttp/promises/src/functions_include.php
new file mode 100644
index 00000000..34cd1710
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/promises/src/functions_include.php
@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\Promise\promise_for')) {
+ require __DIR__ . '/functions.php';
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/.editorconfig b/bin/wiki/vendor/guzzlehttp/psr7/.editorconfig
new file mode 100644
index 00000000..677e36e2
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/CHANGELOG.md b/bin/wiki/vendor/guzzlehttp/psr7/CHANGELOG.md
new file mode 100644
index 00000000..27b65f09
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/CHANGELOG.md
@@ -0,0 +1,225 @@
+# Change Log
+
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+
+
+## [Unreleased]
+
+
+## [1.5.2] - 2018-12-04
+
+### Fixed
+
+- Check body size when getting the message summary
+
+
+## [1.5.1] - 2018-12-04
+
+### Fixed
+
+- Get the summary of a body only if it is readable
+
+
+## [1.5.0] - 2018-12-03
+
+### Added
+
+- Response first-line to response string exception (fixes #145)
+- A test for #129 behavior
+- `get_message_body_summary` function in order to get the message summary
+- `3gp` and `mkv` mime types
+
+### Changed
+
+- Clarify exception message when stream is detached
+
+### Deprecated
+
+- Deprecated parsing folded header lines as per RFC 7230
+
+### Fixed
+
+- Fix `AppendStream::detach` to not close streams
+- `InflateStream` preserves `isSeekable` attribute of the underlying stream
+- `ServerRequest::getUriFromGlobals` to support URLs in query parameters
+
+
+Several other fixes and improvements.
+
+
+## [1.4.2] - 2017-03-20
+
+### Fixed
+
+- Reverted BC break to `Uri::resolve` and `Uri::removeDotSegments` by removing
+ calls to `trigger_error` when deprecated methods are invoked.
+
+
+## [1.4.1] - 2017-02-27
+
+### Added
+
+- Rriggering of silenced deprecation warnings.
+
+### Fixed
+
+- Reverted BC break by reintroducing behavior to automagically fix a URI with a
+ relative path and an authority by adding a leading slash to the path. It's only
+ deprecated now.
+
+
+## [1.4.0] - 2017-02-21
+
+### Added
+
+- Added common URI utility methods based on RFC 3986 (see documentation in the readme):
+ - `Uri::isDefaultPort`
+ - `Uri::isAbsolute`
+ - `Uri::isNetworkPathReference`
+ - `Uri::isAbsolutePathReference`
+ - `Uri::isRelativePathReference`
+ - `Uri::isSameDocumentReference`
+ - `Uri::composeComponents`
+ - `UriNormalizer::normalize`
+ - `UriNormalizer::isEquivalent`
+ - `UriResolver::relativize`
+
+### Changed
+
+- Ensure `ServerRequest::getUriFromGlobals` returns a URI in absolute form.
+- Allow `parse_response` to parse a response without delimiting space and reason.
+- Ensure each URI modification results in a valid URI according to PSR-7 discussions.
+ Invalid modifications will throw an exception instead of returning a wrong URI or
+ doing some magic.
+ - `(new Uri)->withPath('foo')->withHost('example.com')` will throw an exception
+ because the path of a URI with an authority must start with a slash "/" or be empty
+ - `(new Uri())->withScheme('http')` will return `'http://localhost'`
+
+### Deprecated
+
+- `Uri::resolve` in favor of `UriResolver::resolve`
+- `Uri::removeDotSegments` in favor of `UriResolver::removeDotSegments`
+
+### Fixed
+
+- `Stream::read` when length parameter <= 0.
+- `copy_to_stream` reads bytes in chunks instead of `maxLen` into memory.
+- `ServerRequest::getUriFromGlobals` when `Host` header contains port.
+- Compatibility of URIs with `file` scheme and empty host.
+
+
+## [1.3.1] - 2016-06-25
+
+### Fixed
+
+- `Uri::__toString` for network path references, e.g. `//example.org`.
+- Missing lowercase normalization for host.
+- Handling of URI components in case they are `'0'` in a lot of places,
+ e.g. as a user info password.
+- `Uri::withAddedHeader` to correctly merge headers with different case.
+- Trimming of header values in `Uri::withAddedHeader`. Header values may
+ be surrounded by whitespace which should be ignored according to RFC 7230
+ Section 3.2.4. This does not apply to header names.
+- `Uri::withAddedHeader` with an array of header values.
+- `Uri::resolve` when base path has no slash and handling of fragment.
+- Handling of encoding in `Uri::with(out)QueryValue` so one can pass the
+ key/value both in encoded as well as decoded form to those methods. This is
+ consistent with withPath, withQuery etc.
+- `ServerRequest::withoutAttribute` when attribute value is null.
+
+
+## [1.3.0] - 2016-04-13
+
+### Added
+
+- Remaining interfaces needed for full PSR7 compatibility
+ (ServerRequestInterface, UploadedFileInterface, etc.).
+- Support for stream_for from scalars.
+
+### Changed
+
+- Can now extend Uri.
+
+### Fixed
+- A bug in validating request methods by making it more permissive.
+
+
+## [1.2.3] - 2016-02-18
+
+### Fixed
+
+- Support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+ streams, which can sometimes return fewer bytes than requested with `fread`.
+- Handling of gzipped responses with FNAME headers.
+
+
+## [1.2.2] - 2016-01-22
+
+### Added
+
+- Support for URIs without any authority.
+- Support for HTTP 451 'Unavailable For Legal Reasons.'
+- Support for using '0' as a filename.
+- Support for including non-standard ports in Host headers.
+
+
+## [1.2.1] - 2015-11-02
+
+### Changes
+
+- Now supporting negative offsets when seeking to SEEK_END.
+
+
+## [1.2.0] - 2015-08-15
+
+### Changed
+
+- Body as `"0"` is now properly added to a response.
+- Now allowing forward seeking in CachingStream.
+- Now properly parsing HTTP requests that contain proxy targets in
+ `parse_request`.
+- functions.php is now conditionally required.
+- user-info is no longer dropped when resolving URIs.
+
+
+## [1.1.0] - 2015-06-24
+
+### Changed
+
+- URIs can now be relative.
+- `multipart/form-data` headers are now overridden case-insensitively.
+- URI paths no longer encode the following characters because they are allowed
+ in URIs: "(", ")", "*", "!", "'"
+- A port is no longer added to a URI when the scheme is missing and no port is
+ present.
+
+
+## 1.0.0 - 2015-05-19
+
+Initial release.
+
+Currently unsupported:
+
+- `Psr\Http\Message\ServerRequestInterface`
+- `Psr\Http\Message\UploadedFileInterface`
+
+
+
+[Unreleased]: https://github.com/guzzle/psr7/compare/1.5.2...HEAD
+[1.5.2]: https://github.com/guzzle/psr7/compare/1.5.1...1.5.2
+[1.5.1]: https://github.com/guzzle/psr7/compare/1.5.0...1.5.1
+[1.5.0]: https://github.com/guzzle/psr7/compare/1.4.2...1.5.0
+[1.4.2]: https://github.com/guzzle/psr7/compare/1.4.1...1.4.2
+[1.4.1]: https://github.com/guzzle/psr7/compare/1.4.0...1.4.1
+[1.4.0]: https://github.com/guzzle/psr7/compare/1.3.1...1.4.0
+[1.3.1]: https://github.com/guzzle/psr7/compare/1.3.0...1.3.1
+[1.3.0]: https://github.com/guzzle/psr7/compare/1.2.3...1.3.0
+[1.2.3]: https://github.com/guzzle/psr7/compare/1.2.2...1.2.3
+[1.2.2]: https://github.com/guzzle/psr7/compare/1.2.1...1.2.2
+[1.2.1]: https://github.com/guzzle/psr7/compare/1.2.0...1.2.1
+[1.2.0]: https://github.com/guzzle/psr7/compare/1.1.0...1.2.0
+[1.1.0]: https://github.com/guzzle/psr7/compare/1.0.0...1.1.0
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/LICENSE b/bin/wiki/vendor/guzzlehttp/psr7/LICENSE
new file mode 100644
index 00000000..581d95f9
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/README.md b/bin/wiki/vendor/guzzlehttp/psr7/README.md
new file mode 100644
index 00000000..c60a6a38
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/README.md
@@ -0,0 +1,745 @@
+# PSR-7 Message Implementation
+
+This repository contains a full [PSR-7](http://www.php-fig.org/psr/psr-7/)
+message implementation, several stream decorators, and some helpful
+functionality like query string parsing.
+
+
+[![Build Status](https://travis-ci.org/guzzle/psr7.svg?branch=master)](https://travis-ci.org/guzzle/psr7)
+
+
+# Stream implementation
+
+This package comes with a number of stream implementations and stream
+decorators.
+
+
+## AppendStream
+
+`GuzzleHttp\Psr7\AppendStream`
+
+Reads from multiple streams, one after the other.
+
+```php
+use GuzzleHttp\Psr7;
+
+$a = Psr7\stream_for('abc, ');
+$b = Psr7\stream_for('123.');
+$composed = new Psr7\AppendStream([$a, $b]);
+
+$composed->addStream(Psr7\stream_for(' Above all listen to me'));
+
+echo $composed; // abc, 123. Above all listen to me.
+```
+
+
+## BufferStream
+
+`GuzzleHttp\Psr7\BufferStream`
+
+Provides a buffer stream that can be written to fill a buffer, and read
+from to remove bytes from the buffer.
+
+This stream returns a "hwm" metadata value that tells upstream consumers
+what the configured high water mark of the stream is, or the maximum
+preferred size of the buffer.
+
+```php
+use GuzzleHttp\Psr7;
+
+// When more than 1024 bytes are in the buffer, it will begin returning
+// false to writes. This is an indication that writers should slow down.
+$buffer = new Psr7\BufferStream(1024);
+```
+
+
+## CachingStream
+
+The CachingStream is used to allow seeking over previously read bytes on
+non-seekable streams. This can be useful when transferring a non-seekable
+entity body fails due to needing to rewind the stream (for example, resulting
+from a redirect). Data that is read from the remote stream will be buffered in
+a PHP temp stream so that previously read bytes are cached first in memory,
+then on disk.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
+$stream = new Psr7\CachingStream($original);
+
+$stream->read(1024);
+echo $stream->tell();
+// 1024
+
+$stream->seek(0);
+echo $stream->tell();
+// 0
+```
+
+
+## DroppingStream
+
+`GuzzleHttp\Psr7\DroppingStream`
+
+Stream decorator that begins dropping data once the size of the underlying
+stream becomes too full.
+
+```php
+use GuzzleHttp\Psr7;
+
+// Create an empty stream
+$stream = Psr7\stream_for();
+
+// Start dropping data when the stream has more than 10 bytes
+$dropping = new Psr7\DroppingStream($stream, 10);
+
+$dropping->write('01234567890123456789');
+echo $stream; // 0123456789
+```
+
+
+## FnStream
+
+`GuzzleHttp\Psr7\FnStream`
+
+Compose stream implementations based on a hash of functions.
+
+Allows for easy testing and extension of a provided stream without needing
+to create a concrete class for a simple extension point.
+
+```php
+
+use GuzzleHttp\Psr7;
+
+$stream = Psr7\stream_for('hi');
+$fnStream = Psr7\FnStream::decorate($stream, [
+ 'rewind' => function () use ($stream) {
+ echo 'About to rewind - ';
+ $stream->rewind();
+ echo 'rewound!';
+ }
+]);
+
+$fnStream->rewind();
+// Outputs: About to rewind - rewound!
+```
+
+
+## InflateStream
+
+`GuzzleHttp\Psr7\InflateStream`
+
+Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+
+This stream decorator skips the first 10 bytes of the given stream to remove
+the gzip header, converts the provided stream to a PHP stream resource,
+then appends the zlib.inflate filter. The stream is then converted back
+to a Guzzle stream resource to be used as a Guzzle stream.
+
+
+## LazyOpenStream
+
+`GuzzleHttp\Psr7\LazyOpenStream`
+
+Lazily reads or writes to a file that is opened only after an IO operation
+take place on the stream.
+
+```php
+use GuzzleHttp\Psr7;
+
+$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
+// The file has not yet been opened...
+
+echo $stream->read(10);
+// The file is opened and read from only when needed.
+```
+
+
+## LimitStream
+
+`GuzzleHttp\Psr7\LimitStream`
+
+LimitStream can be used to read a subset or slice of an existing stream object.
+This can be useful for breaking a large file into smaller pieces to be sent in
+chunks (e.g. Amazon S3's multipart upload API).
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
+echo $original->getSize();
+// >>> 1048576
+
+// Limit the size of the body to 1024 bytes and start reading from byte 2048
+$stream = new Psr7\LimitStream($original, 1024, 2048);
+echo $stream->getSize();
+// >>> 1024
+echo $stream->tell();
+// >>> 0
+```
+
+
+## MultipartStream
+
+`GuzzleHttp\Psr7\MultipartStream`
+
+Stream that when read returns bytes for a streaming multipart or
+multipart/form-data stream.
+
+
+## NoSeekStream
+
+`GuzzleHttp\Psr7\NoSeekStream`
+
+NoSeekStream wraps a stream and does not allow seeking.
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+$noSeek = new Psr7\NoSeekStream($original);
+
+echo $noSeek->read(3);
+// foo
+var_export($noSeek->isSeekable());
+// false
+$noSeek->seek(0);
+var_export($noSeek->read(3));
+// NULL
+```
+
+
+## PumpStream
+
+`GuzzleHttp\Psr7\PumpStream`
+
+Provides a read only stream that pumps data from a PHP callable.
+
+When invoking the provided callable, the PumpStream will pass the amount of
+data requested to read to the callable. The callable can choose to ignore
+this value and return fewer or more bytes than requested. Any extra data
+returned by the provided callable is buffered internally until drained using
+the read() function of the PumpStream. The provided callable MUST return
+false when there is no more data to read.
+
+
+## Implementing stream decorators
+
+Creating a stream decorator is very easy thanks to the
+`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
+implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
+stream. Just `use` the `StreamDecoratorTrait` and implement your custom
+methods.
+
+For example, let's say we wanted to call a specific function each time the last
+byte is read from a stream. This could be implemented by overriding the
+`read()` method.
+
+```php
+use Psr\Http\Message\StreamInterface;
+use GuzzleHttp\Psr7\StreamDecoratorTrait;
+
+class EofCallbackStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ private $callback;
+
+ public function __construct(StreamInterface $stream, callable $cb)
+ {
+ $this->stream = $stream;
+ $this->callback = $cb;
+ }
+
+ public function read($length)
+ {
+ $result = $this->stream->read($length);
+
+ // Invoke the callback when EOF is hit.
+ if ($this->eof()) {
+ call_user_func($this->callback);
+ }
+
+ return $result;
+ }
+}
+```
+
+This decorator could be added to any existing stream and used like so:
+
+```php
+use GuzzleHttp\Psr7;
+
+$original = Psr7\stream_for('foo');
+
+$eofStream = new EofCallbackStream($original, function () {
+ echo 'EOF!';
+});
+
+$eofStream->read(2);
+$eofStream->read(1);
+// echoes "EOF!"
+$eofStream->seek(0);
+$eofStream->read(3);
+// echoes "EOF!"
+```
+
+
+## PHP StreamWrapper
+
+You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
+PSR-7 stream as a PHP stream resource.
+
+Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
+stream from a PSR-7 stream.
+
+```php
+use GuzzleHttp\Psr7\StreamWrapper;
+
+$stream = GuzzleHttp\Psr7\stream_for('hello!');
+$resource = StreamWrapper::getResource($stream);
+echo fread($resource, 6); // outputs hello!
+```
+
+
+# Function API
+
+There are various functions available under the `GuzzleHttp\Psr7` namespace.
+
+
+## `function str`
+
+`function str(MessageInterface $message)`
+
+Returns the string representation of an HTTP message.
+
+```php
+$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
+echo GuzzleHttp\Psr7\str($request);
+```
+
+
+## `function uri_for`
+
+`function uri_for($uri)`
+
+This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
+UriInterface for the given value. If the value is already a `UriInterface`, it
+is returned as-is.
+
+```php
+$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
+assert($uri === GuzzleHttp\Psr7\uri_for($uri));
+```
+
+
+## `function stream_for`
+
+`function stream_for($resource = '', array $options = [])`
+
+Create a new stream based on the input type.
+
+Options is an associative array that can contain the following keys:
+
+* - metadata: Array of custom metadata.
+* - size: Size of the stream.
+
+This method accepts the following `$resource` types:
+
+- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
+- `string`: Creates a stream object that uses the given string as the contents.
+- `resource`: Creates a stream object that wraps the given PHP stream resource.
+- `Iterator`: If the provided value implements `Iterator`, then a read-only
+ stream object will be created that wraps the given iterable. Each time the
+ stream is read from, data from the iterator will fill a buffer and will be
+ continuously called until the buffer is equal to the requested read size.
+ Subsequent read calls will first read from the buffer and then call `next`
+ on the underlying iterator until it is exhausted.
+- `object` with `__toString()`: If the object has the `__toString()` method,
+ the object will be cast to a string and then a stream will be returned that
+ uses the string value.
+- `NULL`: When `null` is passed, an empty stream object is returned.
+- `callable` When a callable is passed, a read-only stream object will be
+ created that invokes the given callable. The callable is invoked with the
+ number of suggested bytes to read. The callable can return any number of
+ bytes, but MUST return `false` when there is no more data to return. The
+ stream object that wraps the callable will invoke the callable until the
+ number of requested bytes are available. Any additional bytes will be
+ buffered and used in subsequent reads.
+
+```php
+$stream = GuzzleHttp\Psr7\stream_for('foo');
+$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
+
+$generator = function ($bytes) {
+ for ($i = 0; $i < $bytes; $i++) {
+ yield ' ';
+ }
+}
+
+$stream = GuzzleHttp\Psr7\stream_for($generator(100));
+```
+
+
+## `function parse_header`
+
+`function parse_header($header)`
+
+Parse an array of header values containing ";" separated data into an array of
+associative arrays representing the header key value pair data of the header.
+When a parameter does not contain a value, but just contains a key, this
+function will inject a key with a '' string value.
+
+
+## `function normalize_header`
+
+`function normalize_header($header)`
+
+Converts an array of header values that may contain comma separated headers
+into an array of headers with no comma separated values.
+
+
+## `function modify_request`
+
+`function modify_request(RequestInterface $request, array $changes)`
+
+Clone and modify a request with the given changes. This method is useful for
+reducing the number of clones needed to mutate a message.
+
+The changes can be one of:
+
+- method: (string) Changes the HTTP method.
+- set_headers: (array) Sets the given headers.
+- remove_headers: (array) Remove the given headers.
+- body: (mixed) Sets the given body.
+- uri: (UriInterface) Set the URI.
+- query: (string) Set the query string value of the URI.
+- version: (string) Set the protocol version.
+
+
+## `function rewind_body`
+
+`function rewind_body(MessageInterface $message)`
+
+Attempts to rewind a message body and throws an exception on failure. The body
+of the message will only be rewound if a call to `tell()` returns a value other
+than `0`.
+
+
+## `function try_fopen`
+
+`function try_fopen($filename, $mode)`
+
+Safely opens a PHP stream resource using a filename.
+
+When fopen fails, PHP normally raises a warning. This function adds an error
+handler that checks for errors and throws an exception instead.
+
+
+## `function copy_to_string`
+
+`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
+
+Copy the contents of a stream into a string until the given number of bytes
+have been read.
+
+
+## `function copy_to_stream`
+
+`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
+
+Copy the contents of a stream into another stream until the given number of
+bytes have been read.
+
+
+## `function hash`
+
+`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
+
+Calculate a hash of a Stream. This method reads the entire stream to calculate
+a rolling hash (based on PHP's hash_init functions).
+
+
+## `function readline`
+
+`function readline(StreamInterface $stream, $maxLength = null)`
+
+Read a line from the stream up to the maximum allowed buffer length.
+
+
+## `function parse_request`
+
+`function parse_request($message)`
+
+Parses a request message string into a request object.
+
+
+## `function parse_response`
+
+`function parse_response($message)`
+
+Parses a response message string into a response object.
+
+
+## `function parse_query`
+
+`function parse_query($str, $urlEncoding = true)`
+
+Parse a query string into an associative array.
+
+If multiple values are found for the same key, the value of that key value pair
+will become an array. This function does not parse nested PHP style arrays into
+an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
+`['foo[a]' => '1', 'foo[b]' => '2']`).
+
+
+## `function build_query`
+
+`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
+
+Build a query string from an array of key value pairs.
+
+This function can use the return value of parse_query() to build a query string.
+This function does not modify the provided keys when an array is encountered
+(like http_build_query would).
+
+
+## `function mimetype_from_filename`
+
+`function mimetype_from_filename($filename)`
+
+Determines the mimetype of a file by looking at its extension.
+
+
+## `function mimetype_from_extension`
+
+`function mimetype_from_extension($extension)`
+
+Maps a file extensions to a mimetype.
+
+
+# Additional URI Methods
+
+Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
+this library also provides additional functionality when working with URIs as static methods.
+
+## URI Types
+
+An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
+An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
+the base URI. Relative references can be divided into several forms according to
+[RFC 3986 Section 4.2](https://tools.ietf.org/html/rfc3986#section-4.2):
+
+- network-path references, e.g. `//example.com/path`
+- absolute-path references, e.g. `/path`
+- relative-path references, e.g. `subpath`
+
+The following methods can be used to identify the type of the URI.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolute`
+
+`public static function isAbsolute(UriInterface $uri): bool`
+
+Whether the URI is absolute, i.e. it has a scheme.
+
+### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
+
+`public static function isNetworkPathReference(UriInterface $uri): bool`
+
+Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
+termed an network-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
+
+`public static function isAbsolutePathReference(UriInterface $uri): bool`
+
+Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
+termed an absolute-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
+
+`public static function isRelativePathReference(UriInterface $uri): bool`
+
+Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
+termed a relative-path reference.
+
+### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
+
+`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
+
+Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
+fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
+(apart from its fragment) is considered a same-document reference.
+
+## URI Components
+
+Additional methods to work with URI components.
+
+### `GuzzleHttp\Psr7\Uri::isDefaultPort`
+
+`public static function isDefaultPort(UriInterface $uri): bool`
+
+Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
+or the standard port. This method can be used independently of the implementation.
+
+### `GuzzleHttp\Psr7\Uri::composeComponents`
+
+`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
+
+Composes a URI reference string from its various components according to
+[RFC 3986 Section 5.3](https://tools.ietf.org/html/rfc3986#section-5.3). Usually this method does not need to be called
+manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
+
+### `GuzzleHttp\Psr7\Uri::fromParts`
+
+`public static function fromParts(array $parts): UriInterface`
+
+Creates a URI from a hash of [`parse_url`](http://php.net/manual/en/function.parse-url.php) components.
+
+
+### `GuzzleHttp\Psr7\Uri::withQueryValue`
+
+`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
+
+Creates a new URI with a specific query string value. Any existing query string values that exactly match the
+provided key are removed and replaced with the given key value pair. A value of null will set the query string
+key without a value, e.g. "key" instead of "key=value".
+
+### `GuzzleHttp\Psr7\Uri::withQueryValues`
+
+`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
+
+Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
+associative array of key => value.
+
+### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
+
+`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
+
+Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
+provided key are removed.
+
+## Reference Resolution
+
+`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
+to [RFC 3986 Section 5](https://tools.ietf.org/html/rfc3986#section-5). This is for example also what web browsers
+do when resolving a link in a website based on the current request URI.
+
+### `GuzzleHttp\Psr7\UriResolver::resolve`
+
+`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
+
+Converts the relative URI into a new URI that is resolved against the base URI.
+
+### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
+
+`public static function removeDotSegments(string $path): string`
+
+Removes dot segments from a path and returns the new path according to
+[RFC 3986 Section 5.2.4](https://tools.ietf.org/html/rfc3986#section-5.2.4).
+
+### `GuzzleHttp\Psr7\UriResolver::relativize`
+
+`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
+
+Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
+
+```php
+(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+```
+
+One use-case is to use the current request URI as base URI and then generate relative links in your documents
+to reduce the document size or offer self-contained downloadable document archives.
+
+```php
+$base = new Uri('http://example.com/a/b/');
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
+echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
+```
+
+## Normalization and Comparison
+
+`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
+[RFC 3986 Section 6](https://tools.ietf.org/html/rfc3986#section-6).
+
+### `GuzzleHttp\Psr7\UriNormalizer::normalize`
+
+`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
+
+Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
+of normalizations to apply. The following normalizations are available:
+
+- `UriNormalizer::PRESERVING_NORMALIZATIONS`
+
+ Default normalizations which only include the ones that preserve semantics.
+
+- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
+
+ All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+
+ Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
+
+- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
+
+ Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
+ ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
+ not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
+ characters by URI normalizers.
+
+ Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
+
+- `UriNormalizer::CONVERT_EMPTY_PATH`
+
+ Converts the empty path to "/" for http and https URIs.
+
+ Example: `http://example.org` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DEFAULT_HOST`
+
+ Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
+ "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
+ RFC 3986.
+
+ Example: `file://localhost/myfile` → `file:///myfile`
+
+- `UriNormalizer::REMOVE_DEFAULT_PORT`
+
+ Removes the default port of the given URI scheme from the URI.
+
+ Example: `http://example.org:80/` → `http://example.org/`
+
+- `UriNormalizer::REMOVE_DOT_SEGMENTS`
+
+ Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
+ change the semantics of the URI reference.
+
+ Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
+
+- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
+
+ Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
+ and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
+ may change the semantics. Encoded slashes (%2F) are not removed.
+
+ Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
+
+- `UriNormalizer::SORT_QUERY_PARAMETERS`
+
+ Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
+ significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
+ of the URI.
+
+ Example: `?lang=en&article=fred` → `?article=fred&lang=en`
+
+### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
+
+`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
+
+Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
+`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
+This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
+equivalence or difference of relative references does not mean anything.
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/composer.json b/bin/wiki/vendor/guzzlehttp/psr7/composer.json
new file mode 100644
index 00000000..2add153e
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/composer.json
@@ -0,0 +1,45 @@
+{
+ "name": "guzzlehttp/psr7",
+ "type": "library",
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": ["request", "response", "message", "stream", "http", "uri", "url", "psr-7"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": ["src/functions_include.php"]
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "GuzzleHttp\\Tests\\Psr7\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.5-dev"
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/AppendStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/AppendStream.php
new file mode 100644
index 00000000..472a0d61
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/AppendStream.php
@@ -0,0 +1,241 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Reads from multiple streams, one after the other.
+ *
+ * This is a read-only stream decorator.
+ */
+class AppendStream implements StreamInterface
+{
+ /** @var StreamInterface[] Streams being decorated */
+ private $streams = [];
+
+ private $seekable = true;
+ private $current = 0;
+ private $pos = 0;
+
+ /**
+ * @param StreamInterface[] $streams Streams to decorate. Each stream must
+ * be readable.
+ */
+ public function __construct(array $streams = [])
+ {
+ foreach ($streams as $stream) {
+ $this->addStream($stream);
+ }
+ }
+
+ public function __toString()
+ {
+ try {
+ $this->rewind();
+ return $this->getContents();
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ /**
+ * Add a stream to the AppendStream
+ *
+ * @param StreamInterface $stream Stream to append. Must be readable.
+ *
+ * @throws \InvalidArgumentException if the stream is not readable
+ */
+ public function addStream(StreamInterface $stream)
+ {
+ if (!$stream->isReadable()) {
+ throw new \InvalidArgumentException('Each stream must be readable');
+ }
+
+ // The stream is only seekable if all streams are seekable
+ if (!$stream->isSeekable()) {
+ $this->seekable = false;
+ }
+
+ $this->streams[] = $stream;
+ }
+
+ public function getContents()
+ {
+ return copy_to_string($this);
+ }
+
+ /**
+ * Closes each attached stream.
+ *
+ * {@inheritdoc}
+ */
+ public function close()
+ {
+ $this->pos = $this->current = 0;
+ $this->seekable = true;
+
+ foreach ($this->streams as $stream) {
+ $stream->close();
+ }
+
+ $this->streams = [];
+ }
+
+ /**
+ * Detaches each attached stream.
+ *
+ * Returns null as it's not clear which underlying stream resource to return.
+ *
+ * {@inheritdoc}
+ */
+ public function detach()
+ {
+ $this->pos = $this->current = 0;
+ $this->seekable = true;
+
+ foreach ($this->streams as $stream) {
+ $stream->detach();
+ }
+
+ $this->streams = [];
+ }
+
+ public function tell()
+ {
+ return $this->pos;
+ }
+
+ /**
+ * Tries to calculate the size by adding the size of each stream.
+ *
+ * If any of the streams do not return a valid number, then the size of the
+ * append stream cannot be determined and null is returned.
+ *
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ $size = 0;
+
+ foreach ($this->streams as $stream) {
+ $s = $stream->getSize();
+ if ($s === null) {
+ return null;
+ }
+ $size += $s;
+ }
+
+ return $size;
+ }
+
+ public function eof()
+ {
+ return !$this->streams ||
+ ($this->current >= count($this->streams) - 1 &&
+ $this->streams[$this->current]->eof());
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ /**
+ * Attempts to seek to the given position. Only supports SEEK_SET.
+ *
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if (!$this->seekable) {
+ throw new \RuntimeException('This AppendStream is not seekable');
+ } elseif ($whence !== SEEK_SET) {
+ throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
+ }
+
+ $this->pos = $this->current = 0;
+
+ // Rewind each stream
+ foreach ($this->streams as $i => $stream) {
+ try {
+ $stream->rewind();
+ } catch (\Exception $e) {
+ throw new \RuntimeException('Unable to seek stream '
+ . $i . ' of the AppendStream', 0, $e);
+ }
+ }
+
+ // Seek to the actual position by reading from each stream
+ while ($this->pos < $offset && !$this->eof()) {
+ $result = $this->read(min(8096, $offset - $this->pos));
+ if ($result === '') {
+ break;
+ }
+ }
+ }
+
+ /**
+ * Reads from all of the appended streams until the length is met or EOF.
+ *
+ * {@inheritdoc}
+ */
+ public function read($length)
+ {
+ $buffer = '';
+ $total = count($this->streams) - 1;
+ $remaining = $length;
+ $progressToNext = false;
+
+ while ($remaining > 0) {
+
+ // Progress to the next stream if needed.
+ if ($progressToNext || $this->streams[$this->current]->eof()) {
+ $progressToNext = false;
+ if ($this->current === $total) {
+ break;
+ }
+ $this->current++;
+ }
+
+ $result = $this->streams[$this->current]->read($remaining);
+
+ // Using a loose comparison here to match on '', false, and null
+ if ($result == null) {
+ $progressToNext = true;
+ continue;
+ }
+
+ $buffer .= $result;
+ $remaining = $length - strlen($buffer);
+ }
+
+ $this->pos += strlen($buffer);
+
+ return $buffer;
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ public function isSeekable()
+ {
+ return $this->seekable;
+ }
+
+ public function write($string)
+ {
+ throw new \RuntimeException('Cannot write to an AppendStream');
+ }
+
+ public function getMetadata($key = null)
+ {
+ return $key ? null : [];
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/BufferStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/BufferStream.php
new file mode 100644
index 00000000..af4d4c22
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/BufferStream.php
@@ -0,0 +1,137 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a buffer stream that can be written to to fill a buffer, and read
+ * from to remove bytes from the buffer.
+ *
+ * This stream returns a "hwm" metadata value that tells upstream consumers
+ * what the configured high water mark of the stream is, or the maximum
+ * preferred size of the buffer.
+ */
+class BufferStream implements StreamInterface
+{
+ private $hwm;
+ private $buffer = '';
+
+ /**
+ * @param int $hwm High water mark, representing the preferred maximum
+ * buffer size. If the size of the buffer exceeds the high
+ * water mark, then calls to write will continue to succeed
+ * but will return false to inform writers to slow down
+ * until the buffer has been drained by reading from it.
+ */
+ public function __construct($hwm = 16384)
+ {
+ $this->hwm = $hwm;
+ }
+
+ public function __toString()
+ {
+ return $this->getContents();
+ }
+
+ public function getContents()
+ {
+ $buffer = $this->buffer;
+ $this->buffer = '';
+
+ return $buffer;
+ }
+
+ public function close()
+ {
+ $this->buffer = '';
+ }
+
+ public function detach()
+ {
+ $this->close();
+ }
+
+ public function getSize()
+ {
+ return strlen($this->buffer);
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function isWritable()
+ {
+ return true;
+ }
+
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \RuntimeException('Cannot seek a BufferStream');
+ }
+
+ public function eof()
+ {
+ return strlen($this->buffer) === 0;
+ }
+
+ public function tell()
+ {
+ throw new \RuntimeException('Cannot determine the position of a BufferStream');
+ }
+
+ /**
+ * Reads data from the buffer.
+ */
+ public function read($length)
+ {
+ $currentLength = strlen($this->buffer);
+
+ if ($length >= $currentLength) {
+ // No need to slice the buffer because we don't have enough data.
+ $result = $this->buffer;
+ $this->buffer = '';
+ } else {
+ // Slice up the result to provide a subset of the buffer.
+ $result = substr($this->buffer, 0, $length);
+ $this->buffer = substr($this->buffer, $length);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Writes data to the buffer.
+ */
+ public function write($string)
+ {
+ $this->buffer .= $string;
+
+ // TODO: What should happen here?
+ if (strlen($this->buffer) >= $this->hwm) {
+ return false;
+ }
+
+ return strlen($string);
+ }
+
+ public function getMetadata($key = null)
+ {
+ if ($key == 'hwm') {
+ return $this->hwm;
+ }
+
+ return $key ? null : [];
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/CachingStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/CachingStream.php
new file mode 100644
index 00000000..ed68f086
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/CachingStream.php
@@ -0,0 +1,138 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that can cache previously read bytes from a sequentially
+ * read stream.
+ */
+class CachingStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var StreamInterface Stream being wrapped */
+ private $remoteStream;
+
+ /** @var int Number of bytes to skip reading due to a write on the buffer */
+ private $skipReadBytes = 0;
+
+ /**
+ * We will treat the buffer object as the body of the stream
+ *
+ * @param StreamInterface $stream Stream to cache
+ * @param StreamInterface $target Optionally specify where data is cached
+ */
+ public function __construct(
+ StreamInterface $stream,
+ StreamInterface $target = null
+ ) {
+ $this->remoteStream = $stream;
+ $this->stream = $target ?: new Stream(fopen('php://temp', 'r+'));
+ }
+
+ public function getSize()
+ {
+ return max($this->stream->getSize(), $this->remoteStream->getSize());
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if ($whence == SEEK_SET) {
+ $byte = $offset;
+ } elseif ($whence == SEEK_CUR) {
+ $byte = $offset + $this->tell();
+ } elseif ($whence == SEEK_END) {
+ $size = $this->remoteStream->getSize();
+ if ($size === null) {
+ $size = $this->cacheEntireStream();
+ }
+ $byte = $size + $offset;
+ } else {
+ throw new \InvalidArgumentException('Invalid whence');
+ }
+
+ $diff = $byte - $this->stream->getSize();
+
+ if ($diff > 0) {
+ // Read the remoteStream until we have read in at least the amount
+ // of bytes requested, or we reach the end of the file.
+ while ($diff > 0 && !$this->remoteStream->eof()) {
+ $this->read($diff);
+ $diff = $byte - $this->stream->getSize();
+ }
+ } else {
+ // We can just do a normal seek since we've already seen this byte.
+ $this->stream->seek($byte);
+ }
+ }
+
+ public function read($length)
+ {
+ // Perform a regular read on any previously read data from the buffer
+ $data = $this->stream->read($length);
+ $remaining = $length - strlen($data);
+
+ // More data was requested so read from the remote stream
+ if ($remaining) {
+ // If data was written to the buffer in a position that would have
+ // been filled from the remote stream, then we must skip bytes on
+ // the remote stream to emulate overwriting bytes from that
+ // position. This mimics the behavior of other PHP stream wrappers.
+ $remoteData = $this->remoteStream->read(
+ $remaining + $this->skipReadBytes
+ );
+
+ if ($this->skipReadBytes) {
+ $len = strlen($remoteData);
+ $remoteData = substr($remoteData, $this->skipReadBytes);
+ $this->skipReadBytes = max(0, $this->skipReadBytes - $len);
+ }
+
+ $data .= $remoteData;
+ $this->stream->write($remoteData);
+ }
+
+ return $data;
+ }
+
+ public function write($string)
+ {
+ // When appending to the end of the currently read stream, you'll want
+ // to skip bytes from being read from the remote stream to emulate
+ // other stream wrappers. Basically replacing bytes of data of a fixed
+ // length.
+ $overflow = (strlen($string) + $this->tell()) - $this->remoteStream->tell();
+ if ($overflow > 0) {
+ $this->skipReadBytes += $overflow;
+ }
+
+ return $this->stream->write($string);
+ }
+
+ public function eof()
+ {
+ return $this->stream->eof() && $this->remoteStream->eof();
+ }
+
+ /**
+ * Close both the remote stream and buffer stream
+ */
+ public function close()
+ {
+ $this->remoteStream->close() && $this->stream->close();
+ }
+
+ private function cacheEntireStream()
+ {
+ $target = new FnStream(['write' => 'strlen']);
+ copy_to_stream($this, $target);
+
+ return $this->tell();
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/DroppingStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/DroppingStream.php
new file mode 100644
index 00000000..8935c80d
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/DroppingStream.php
@@ -0,0 +1,42 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that begins dropping data once the size of the underlying
+ * stream becomes too full.
+ */
+class DroppingStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ private $maxLength;
+
+ /**
+ * @param StreamInterface $stream Underlying stream to decorate.
+ * @param int $maxLength Maximum size before dropping data.
+ */
+ public function __construct(StreamInterface $stream, $maxLength)
+ {
+ $this->stream = $stream;
+ $this->maxLength = $maxLength;
+ }
+
+ public function write($string)
+ {
+ $diff = $this->maxLength - $this->stream->getSize();
+
+ // Begin returning 0 when the underlying stream is too large.
+ if ($diff <= 0) {
+ return 0;
+ }
+
+ // Write the stream or a subset of the stream if needed.
+ if (strlen($string) < $diff) {
+ return $this->stream->write($string);
+ }
+
+ return $this->stream->write(substr($string, 0, $diff));
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/FnStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/FnStream.php
new file mode 100644
index 00000000..73daea6f
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/FnStream.php
@@ -0,0 +1,158 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Compose stream implementations based on a hash of functions.
+ *
+ * Allows for easy testing and extension of a provided stream without needing
+ * to create a concrete class for a simple extension point.
+ */
+class FnStream implements StreamInterface
+{
+ /** @var array */
+ private $methods;
+
+ /** @var array Methods that must be implemented in the given array */
+ private static $slots = ['__toString', 'close', 'detach', 'rewind',
+ 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
+ 'isReadable', 'read', 'getContents', 'getMetadata'];
+
+ /**
+ * @param array $methods Hash of method name to a callable.
+ */
+ public function __construct(array $methods)
+ {
+ $this->methods = $methods;
+
+ // Create the functions on the class
+ foreach ($methods as $name => $fn) {
+ $this->{'_fn_' . $name} = $fn;
+ }
+ }
+
+ /**
+ * Lazily determine which methods are not implemented.
+ * @throws \BadMethodCallException
+ */
+ public function __get($name)
+ {
+ throw new \BadMethodCallException(str_replace('_fn_', '', $name)
+ . '() is not implemented in the FnStream');
+ }
+
+ /**
+ * The close method is called on the underlying stream only if possible.
+ */
+ public function __destruct()
+ {
+ if (isset($this->_fn_close)) {
+ call_user_func($this->_fn_close);
+ }
+ }
+
+ /**
+ * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
+ * @throws \LogicException
+ */
+ public function __wakeup()
+ {
+ throw new \LogicException('FnStream should never be unserialized');
+ }
+
+ /**
+ * Adds custom functionality to an underlying stream by intercepting
+ * specific method calls.
+ *
+ * @param StreamInterface $stream Stream to decorate
+ * @param array $methods Hash of method name to a closure
+ *
+ * @return FnStream
+ */
+ public static function decorate(StreamInterface $stream, array $methods)
+ {
+ // If any of the required methods were not provided, then simply
+ // proxy to the decorated stream.
+ foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
+ $methods[$diff] = [$stream, $diff];
+ }
+
+ return new self($methods);
+ }
+
+ public function __toString()
+ {
+ return call_user_func($this->_fn___toString);
+ }
+
+ public function close()
+ {
+ return call_user_func($this->_fn_close);
+ }
+
+ public function detach()
+ {
+ return call_user_func($this->_fn_detach);
+ }
+
+ public function getSize()
+ {
+ return call_user_func($this->_fn_getSize);
+ }
+
+ public function tell()
+ {
+ return call_user_func($this->_fn_tell);
+ }
+
+ public function eof()
+ {
+ return call_user_func($this->_fn_eof);
+ }
+
+ public function isSeekable()
+ {
+ return call_user_func($this->_fn_isSeekable);
+ }
+
+ public function rewind()
+ {
+ call_user_func($this->_fn_rewind);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ call_user_func($this->_fn_seek, $offset, $whence);
+ }
+
+ public function isWritable()
+ {
+ return call_user_func($this->_fn_isWritable);
+ }
+
+ public function write($string)
+ {
+ return call_user_func($this->_fn_write, $string);
+ }
+
+ public function isReadable()
+ {
+ return call_user_func($this->_fn_isReadable);
+ }
+
+ public function read($length)
+ {
+ return call_user_func($this->_fn_read, $length);
+ }
+
+ public function getContents()
+ {
+ return call_user_func($this->_fn_getContents);
+ }
+
+ public function getMetadata($key = null)
+ {
+ return call_user_func($this->_fn_getMetadata, $key);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/InflateStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/InflateStream.php
new file mode 100644
index 00000000..5e4f6028
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/InflateStream.php
@@ -0,0 +1,52 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Uses PHP's zlib.inflate filter to inflate deflate or gzipped content.
+ *
+ * This stream decorator skips the first 10 bytes of the given stream to remove
+ * the gzip header, converts the provided stream to a PHP stream resource,
+ * then appends the zlib.inflate filter. The stream is then converted back
+ * to a Guzzle stream resource to be used as a Guzzle stream.
+ *
+ * @link http://tools.ietf.org/html/rfc1952
+ * @link http://php.net/manual/en/filters.compression.php
+ */
+class InflateStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ public function __construct(StreamInterface $stream)
+ {
+ // read the first 10 bytes, ie. gzip header
+ $header = $stream->read(10);
+ $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
+ // Skip the header, that is 10 + length of filename + 1 (nil) bytes
+ $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
+ $resource = StreamWrapper::getResource($stream);
+ stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
+ $this->stream = $stream->isSeekable() ? new Stream($resource) : new NoSeekStream(new Stream($resource));
+ }
+
+ /**
+ * @param StreamInterface $stream
+ * @param $header
+ * @return int
+ */
+ private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
+ {
+ $filename_header_length = 0;
+
+ if (substr(bin2hex($header), 6, 2) === '08') {
+ // we have a filename, read until nil
+ $filename_header_length = 1;
+ while ($stream->read(1) !== chr(0)) {
+ $filename_header_length++;
+ }
+ }
+
+ return $filename_header_length;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/LazyOpenStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
new file mode 100644
index 00000000..02cec3af
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/LazyOpenStream.php
@@ -0,0 +1,39 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Lazily reads or writes to a file that is opened only after an IO operation
+ * take place on the stream.
+ */
+class LazyOpenStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var string File to open */
+ private $filename;
+
+ /** @var string $mode */
+ private $mode;
+
+ /**
+ * @param string $filename File to lazily open
+ * @param string $mode fopen mode to use when opening the stream
+ */
+ public function __construct($filename, $mode)
+ {
+ $this->filename = $filename;
+ $this->mode = $mode;
+ }
+
+ /**
+ * Creates the underlying stream lazily when required.
+ *
+ * @return StreamInterface
+ */
+ protected function createStream()
+ {
+ return stream_for(try_fopen($this->filename, $this->mode));
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/LimitStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/LimitStream.php
new file mode 100644
index 00000000..3c13d4f4
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/LimitStream.php
@@ -0,0 +1,155 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+
+/**
+ * Decorator used to return only a subset of a stream
+ */
+class LimitStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ /** @var int Offset to start reading from */
+ private $offset;
+
+ /** @var int Limit the number of bytes that can be read */
+ private $limit;
+
+ /**
+ * @param StreamInterface $stream Stream to wrap
+ * @param int $limit Total number of bytes to allow to be read
+ * from the stream. Pass -1 for no limit.
+ * @param int $offset Position to seek to before reading (only
+ * works on seekable streams).
+ */
+ public function __construct(
+ StreamInterface $stream,
+ $limit = -1,
+ $offset = 0
+ ) {
+ $this->stream = $stream;
+ $this->setLimit($limit);
+ $this->setOffset($offset);
+ }
+
+ public function eof()
+ {
+ // Always return true if the underlying stream is EOF
+ if ($this->stream->eof()) {
+ return true;
+ }
+
+ // No limit and the underlying stream is not at EOF
+ if ($this->limit == -1) {
+ return false;
+ }
+
+ return $this->stream->tell() >= $this->offset + $this->limit;
+ }
+
+ /**
+ * Returns the size of the limited subset of data
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ if (null === ($length = $this->stream->getSize())) {
+ return null;
+ } elseif ($this->limit == -1) {
+ return $length - $this->offset;
+ } else {
+ return min($this->limit, $length - $this->offset);
+ }
+ }
+
+ /**
+ * Allow for a bounded seek on the read limited stream
+ * {@inheritdoc}
+ */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if ($whence !== SEEK_SET || $offset < 0) {
+ throw new \RuntimeException(sprintf(
+ 'Cannot seek to offset % with whence %s',
+ $offset,
+ $whence
+ ));
+ }
+
+ $offset += $this->offset;
+
+ if ($this->limit !== -1) {
+ if ($offset > $this->offset + $this->limit) {
+ $offset = $this->offset + $this->limit;
+ }
+ }
+
+ $this->stream->seek($offset);
+ }
+
+ /**
+ * Give a relative tell()
+ * {@inheritdoc}
+ */
+ public function tell()
+ {
+ return $this->stream->tell() - $this->offset;
+ }
+
+ /**
+ * Set the offset to start limiting from
+ *
+ * @param int $offset Offset to seek to and begin byte limiting from
+ *
+ * @throws \RuntimeException if the stream cannot be seeked.
+ */
+ public function setOffset($offset)
+ {
+ $current = $this->stream->tell();
+
+ if ($current !== $offset) {
+ // If the stream cannot seek to the offset position, then read to it
+ if ($this->stream->isSeekable()) {
+ $this->stream->seek($offset);
+ } elseif ($current > $offset) {
+ throw new \RuntimeException("Could not seek to stream offset $offset");
+ } else {
+ $this->stream->read($offset - $current);
+ }
+ }
+
+ $this->offset = $offset;
+ }
+
+ /**
+ * Set the limit of bytes that the decorator allows to be read from the
+ * stream.
+ *
+ * @param int $limit Number of bytes to allow to be read from the stream.
+ * Use -1 for no limit.
+ */
+ public function setLimit($limit)
+ {
+ $this->limit = $limit;
+ }
+
+ public function read($length)
+ {
+ if ($this->limit == -1) {
+ return $this->stream->read($length);
+ }
+
+ // Check if the current position is less than the total allowed
+ // bytes + original offset
+ $remaining = ($this->offset + $this->limit) - $this->stream->tell();
+ if ($remaining > 0) {
+ // Only return the amount of requested data, ensuring that the byte
+ // limit is not exceeded
+ return $this->stream->read(min($remaining, $length));
+ }
+
+ return '';
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/MessageTrait.php b/bin/wiki/vendor/guzzlehttp/psr7/src/MessageTrait.php
new file mode 100644
index 00000000..1e4da649
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/MessageTrait.php
@@ -0,0 +1,183 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Trait implementing functionality common to requests and responses.
+ */
+trait MessageTrait
+{
+ /** @var array Map of all registered headers, as original name => array of values */
+ private $headers = [];
+
+ /** @var array Map of lowercase header name => original name at registration */
+ private $headerNames = [];
+
+ /** @var string */
+ private $protocol = '1.1';
+
+ /** @var StreamInterface */
+ private $stream;
+
+ public function getProtocolVersion()
+ {
+ return $this->protocol;
+ }
+
+ public function withProtocolVersion($version)
+ {
+ if ($this->protocol === $version) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->protocol = $version;
+ return $new;
+ }
+
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ public function hasHeader($header)
+ {
+ return isset($this->headerNames[strtolower($header)]);
+ }
+
+ public function getHeader($header)
+ {
+ $header = strtolower($header);
+
+ if (!isset($this->headerNames[$header])) {
+ return [];
+ }
+
+ $header = $this->headerNames[$header];
+
+ return $this->headers[$header];
+ }
+
+ public function getHeaderLine($header)
+ {
+ return implode(', ', $this->getHeader($header));
+ }
+
+ public function withHeader($header, $value)
+ {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+
+ $value = $this->trimHeaderValues($value);
+ $normalized = strtolower($header);
+
+ $new = clone $this;
+ if (isset($new->headerNames[$normalized])) {
+ unset($new->headers[$new->headerNames[$normalized]]);
+ }
+ $new->headerNames[$normalized] = $header;
+ $new->headers[$header] = $value;
+
+ return $new;
+ }
+
+ public function withAddedHeader($header, $value)
+ {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+
+ $value = $this->trimHeaderValues($value);
+ $normalized = strtolower($header);
+
+ $new = clone $this;
+ if (isset($new->headerNames[$normalized])) {
+ $header = $this->headerNames[$normalized];
+ $new->headers[$header] = array_merge($this->headers[$header], $value);
+ } else {
+ $new->headerNames[$normalized] = $header;
+ $new->headers[$header] = $value;
+ }
+
+ return $new;
+ }
+
+ public function withoutHeader($header)
+ {
+ $normalized = strtolower($header);
+
+ if (!isset($this->headerNames[$normalized])) {
+ return $this;
+ }
+
+ $header = $this->headerNames[$normalized];
+
+ $new = clone $this;
+ unset($new->headers[$header], $new->headerNames[$normalized]);
+
+ return $new;
+ }
+
+ public function getBody()
+ {
+ if (!$this->stream) {
+ $this->stream = stream_for('');
+ }
+
+ return $this->stream;
+ }
+
+ public function withBody(StreamInterface $body)
+ {
+ if ($body === $this->stream) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->stream = $body;
+ return $new;
+ }
+
+ private function setHeaders(array $headers)
+ {
+ $this->headerNames = $this->headers = [];
+ foreach ($headers as $header => $value) {
+ if (!is_array($value)) {
+ $value = [$value];
+ }
+
+ $value = $this->trimHeaderValues($value);
+ $normalized = strtolower($header);
+ if (isset($this->headerNames[$normalized])) {
+ $header = $this->headerNames[$normalized];
+ $this->headers[$header] = array_merge($this->headers[$header], $value);
+ } else {
+ $this->headerNames[$normalized] = $header;
+ $this->headers[$header] = $value;
+ }
+ }
+ }
+
+ /**
+ * Trims whitespace from the header values.
+ *
+ * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field.
+ *
+ * header-field = field-name ":" OWS field-value OWS
+ * OWS = *( SP / HTAB )
+ *
+ * @param string[] $values Header values
+ *
+ * @return string[] Trimmed header values
+ *
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2.4
+ */
+ private function trimHeaderValues(array $values)
+ {
+ return array_map(function ($value) {
+ return trim($value, " \t");
+ }, $values);
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/MultipartStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/MultipartStream.php
new file mode 100644
index 00000000..c0fd584f
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/MultipartStream.php
@@ -0,0 +1,153 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream that when read returns bytes for a streaming multipart or
+ * multipart/form-data stream.
+ */
+class MultipartStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ private $boundary;
+
+ /**
+ * @param array $elements Array of associative arrays, each containing a
+ * required "name" key mapping to the form field,
+ * name, a required "contents" key mapping to a
+ * StreamInterface/resource/string, an optional
+ * "headers" associative array of custom headers,
+ * and an optional "filename" key mapping to a
+ * string to send as the filename in the part.
+ * @param string $boundary You can optionally provide a specific boundary
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct(array $elements = [], $boundary = null)
+ {
+ $this->boundary = $boundary ?: sha1(uniqid('', true));
+ $this->stream = $this->createStream($elements);
+ }
+
+ /**
+ * Get the boundary
+ *
+ * @return string
+ */
+ public function getBoundary()
+ {
+ return $this->boundary;
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ /**
+ * Get the headers needed before transferring the content of a POST file
+ */
+ private function getHeaders(array $headers)
+ {
+ $str = '';
+ foreach ($headers as $key => $value) {
+ $str .= "{$key}: {$value}\r\n";
+ }
+
+ return "--{$this->boundary}\r\n" . trim($str) . "\r\n\r\n";
+ }
+
+ /**
+ * Create the aggregate stream that will be used to upload the POST data
+ */
+ protected function createStream(array $elements)
+ {
+ $stream = new AppendStream();
+
+ foreach ($elements as $element) {
+ $this->addElement($stream, $element);
+ }
+
+ // Add the trailing boundary with CRLF
+ $stream->addStream(stream_for("--{$this->boundary}--\r\n"));
+
+ return $stream;
+ }
+
+ private function addElement(AppendStream $stream, array $element)
+ {
+ foreach (['contents', 'name'] as $key) {
+ if (!array_key_exists($key, $element)) {
+ throw new \InvalidArgumentException("A '{$key}' key is required");
+ }
+ }
+
+ $element['contents'] = stream_for($element['contents']);
+
+ if (empty($element['filename'])) {
+ $uri = $element['contents']->getMetadata('uri');
+ if (substr($uri, 0, 6) !== 'php://') {
+ $element['filename'] = $uri;
+ }
+ }
+
+ list($body, $headers) = $this->createElement(
+ $element['name'],
+ $element['contents'],
+ isset($element['filename']) ? $element['filename'] : null,
+ isset($element['headers']) ? $element['headers'] : []
+ );
+
+ $stream->addStream(stream_for($this->getHeaders($headers)));
+ $stream->addStream($body);
+ $stream->addStream(stream_for("\r\n"));
+ }
+
+ /**
+ * @return array
+ */
+ private function createElement($name, StreamInterface $stream, $filename, array $headers)
+ {
+ // Set a default content-disposition header if one was no provided
+ $disposition = $this->getHeader($headers, 'content-disposition');
+ if (!$disposition) {
+ $headers['Content-Disposition'] = ($filename === '0' || $filename)
+ ? sprintf('form-data; name="%s"; filename="%s"',
+ $name,
+ basename($filename))
+ : "form-data; name=\"{$name}\"";
+ }
+
+ // Set a default content-length header if one was no provided
+ $length = $this->getHeader($headers, 'content-length');
+ if (!$length) {
+ if ($length = $stream->getSize()) {
+ $headers['Content-Length'] = (string) $length;
+ }
+ }
+
+ // Set a default Content-Type if one was not supplied
+ $type = $this->getHeader($headers, 'content-type');
+ if (!$type && ($filename === '0' || $filename)) {
+ if ($type = mimetype_from_filename($filename)) {
+ $headers['Content-Type'] = $type;
+ }
+ }
+
+ return [$stream, $headers];
+ }
+
+ private function getHeader(array $headers, $key)
+ {
+ $lowercaseHeader = strtolower($key);
+ foreach ($headers as $k => $v) {
+ if (strtolower($k) === $lowercaseHeader) {
+ return $v;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/NoSeekStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/NoSeekStream.php
new file mode 100644
index 00000000..23322180
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/NoSeekStream.php
@@ -0,0 +1,22 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator that prevents a stream from being seeked
+ */
+class NoSeekStream implements StreamInterface
+{
+ use StreamDecoratorTrait;
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \RuntimeException('Cannot seek a NoSeekStream');
+ }
+
+ public function isSeekable()
+ {
+ return false;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/PumpStream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/PumpStream.php
new file mode 100644
index 00000000..ffb5440d
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/PumpStream.php
@@ -0,0 +1,165 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Provides a read only stream that pumps data from a PHP callable.
+ *
+ * When invoking the provided callable, the PumpStream will pass the amount of
+ * data requested to read to the callable. The callable can choose to ignore
+ * this value and return fewer or more bytes than requested. Any extra data
+ * returned by the provided callable is buffered internally until drained using
+ * the read() function of the PumpStream. The provided callable MUST return
+ * false when there is no more data to read.
+ */
+class PumpStream implements StreamInterface
+{
+ /** @var callable */
+ private $source;
+
+ /** @var int */
+ private $size;
+
+ /** @var int */
+ private $tellPos = 0;
+
+ /** @var array */
+ private $metadata;
+
+ /** @var BufferStream */
+ private $buffer;
+
+ /**
+ * @param callable $source Source of the stream data. The callable MAY
+ * accept an integer argument used to control the
+ * amount of data to return. The callable MUST
+ * return a string when called, or false on error
+ * or EOF.
+ * @param array $options Stream options:
+ * - metadata: Hash of metadata to use with stream.
+ * - size: Size of the stream, if known.
+ */
+ public function __construct(callable $source, array $options = [])
+ {
+ $this->source = $source;
+ $this->size = isset($options['size']) ? $options['size'] : null;
+ $this->metadata = isset($options['metadata']) ? $options['metadata'] : [];
+ $this->buffer = new BufferStream();
+ }
+
+ public function __toString()
+ {
+ try {
+ return copy_to_string($this);
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ public function close()
+ {
+ $this->detach();
+ }
+
+ public function detach()
+ {
+ $this->tellPos = false;
+ $this->source = null;
+ }
+
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ public function tell()
+ {
+ return $this->tellPos;
+ }
+
+ public function eof()
+ {
+ return !$this->source;
+ }
+
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \RuntimeException('Cannot seek a PumpStream');
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ public function write($string)
+ {
+ throw new \RuntimeException('Cannot write to a PumpStream');
+ }
+
+ public function isReadable()
+ {
+ return true;
+ }
+
+ public function read($length)
+ {
+ $data = $this->buffer->read($length);
+ $readLen = strlen($data);
+ $this->tellPos += $readLen;
+ $remaining = $length - $readLen;
+
+ if ($remaining) {
+ $this->pump($remaining);
+ $data .= $this->buffer->read($remaining);
+ $this->tellPos += strlen($data) - $readLen;
+ }
+
+ return $data;
+ }
+
+ public function getContents()
+ {
+ $result = '';
+ while (!$this->eof()) {
+ $result .= $this->read(1000000);
+ }
+
+ return $result;
+ }
+
+ public function getMetadata($key = null)
+ {
+ if (!$key) {
+ return $this->metadata;
+ }
+
+ return isset($this->metadata[$key]) ? $this->metadata[$key] : null;
+ }
+
+ private function pump($length)
+ {
+ if ($this->source) {
+ do {
+ $data = call_user_func($this->source, $length);
+ if ($data === false || $data === null) {
+ $this->source = null;
+ return;
+ }
+ $this->buffer->write($data);
+ $length -= strlen($data);
+ } while ($length > 0);
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/Request.php b/bin/wiki/vendor/guzzlehttp/psr7/src/Request.php
new file mode 100644
index 00000000..00066424
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/Request.php
@@ -0,0 +1,142 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 request implementation.
+ */
+class Request implements RequestInterface
+{
+ use MessageTrait;
+
+ /** @var string */
+ private $method;
+
+ /** @var null|string */
+ private $requestTarget;
+
+ /** @var UriInterface */
+ private $uri;
+
+ /**
+ * @param string $method HTTP method
+ * @param string|UriInterface $uri URI
+ * @param array $headers Request headers
+ * @param string|null|resource|StreamInterface $body Request body
+ * @param string $version Protocol version
+ */
+ public function __construct(
+ $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ $version = '1.1'
+ ) {
+ if (!($uri instanceof UriInterface)) {
+ $uri = new Uri($uri);
+ }
+
+ $this->method = strtoupper($method);
+ $this->uri = $uri;
+ $this->setHeaders($headers);
+ $this->protocol = $version;
+
+ if (!isset($this->headerNames['host'])) {
+ $this->updateHostFromUri();
+ }
+
+ if ($body !== '' && $body !== null) {
+ $this->stream = stream_for($body);
+ }
+ }
+
+ public function getRequestTarget()
+ {
+ if ($this->requestTarget !== null) {
+ return $this->requestTarget;
+ }
+
+ $target = $this->uri->getPath();
+ if ($target == '') {
+ $target = '/';
+ }
+ if ($this->uri->getQuery() != '') {
+ $target .= '?' . $this->uri->getQuery();
+ }
+
+ return $target;
+ }
+
+ public function withRequestTarget($requestTarget)
+ {
+ if (preg_match('#\s#', $requestTarget)) {
+ throw new InvalidArgumentException(
+ 'Invalid request target provided; cannot contain whitespace'
+ );
+ }
+
+ $new = clone $this;
+ $new->requestTarget = $requestTarget;
+ return $new;
+ }
+
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ public function withMethod($method)
+ {
+ $new = clone $this;
+ $new->method = strtoupper($method);
+ return $new;
+ }
+
+ public function getUri()
+ {
+ return $this->uri;
+ }
+
+ public function withUri(UriInterface $uri, $preserveHost = false)
+ {
+ if ($uri === $this->uri) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->uri = $uri;
+
+ if (!$preserveHost || !isset($this->headerNames['host'])) {
+ $new->updateHostFromUri();
+ }
+
+ return $new;
+ }
+
+ private function updateHostFromUri()
+ {
+ $host = $this->uri->getHost();
+
+ if ($host == '') {
+ return;
+ }
+
+ if (($port = $this->uri->getPort()) !== null) {
+ $host .= ':' . $port;
+ }
+
+ if (isset($this->headerNames['host'])) {
+ $header = $this->headerNames['host'];
+ } else {
+ $header = 'Host';
+ $this->headerNames['host'] = 'Host';
+ }
+ // Ensure Host is the first header.
+ // See: http://tools.ietf.org/html/rfc7230#section-5.4
+ $this->headers = [$header => [$host]] + $this->headers;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/Response.php b/bin/wiki/vendor/guzzlehttp/psr7/src/Response.php
new file mode 100644
index 00000000..6e72c06b
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/Response.php
@@ -0,0 +1,136 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PSR-7 response implementation.
+ */
+class Response implements ResponseInterface
+{
+ use MessageTrait;
+
+ /** @var array Map of standard HTTP status code/reason phrases */
+ private static $phrases = [
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-status',
+ 208 => 'Already Reported',
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 306 => 'Switch Proxy',
+ 307 => 'Temporary Redirect',
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Time-out',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Request Entity Too Large',
+ 414 => 'Request-URI Too Large',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Requested range not satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot',
+ 422 => 'Unprocessable Entity',
+ 423 => 'Locked',
+ 424 => 'Failed Dependency',
+ 425 => 'Unordered Collection',
+ 426 => 'Upgrade Required',
+ 428 => 'Precondition Required',
+ 429 => 'Too Many Requests',
+ 431 => 'Request Header Fields Too Large',
+ 451 => 'Unavailable For Legal Reasons',
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Time-out',
+ 505 => 'HTTP Version not supported',
+ 506 => 'Variant Also Negotiates',
+ 507 => 'Insufficient Storage',
+ 508 => 'Loop Detected',
+ 511 => 'Network Authentication Required',
+ ];
+
+ /** @var string */
+ private $reasonPhrase = '';
+
+ /** @var int */
+ private $statusCode = 200;
+
+ /**
+ * @param int $status Status code
+ * @param array $headers Response headers
+ * @param string|null|resource|StreamInterface $body Response body
+ * @param string $version Protocol version
+ * @param string|null $reason Reason phrase (when empty a default will be used based on the status code)
+ */
+ public function __construct(
+ $status = 200,
+ array $headers = [],
+ $body = null,
+ $version = '1.1',
+ $reason = null
+ ) {
+ if (filter_var($status, FILTER_VALIDATE_INT) === false) {
+ throw new \InvalidArgumentException('Status code must be an integer value.');
+ }
+
+ $this->statusCode = (int) $status;
+
+ if ($body !== '' && $body !== null) {
+ $this->stream = stream_for($body);
+ }
+
+ $this->setHeaders($headers);
+ if ($reason == '' && isset(self::$phrases[$this->statusCode])) {
+ $this->reasonPhrase = self::$phrases[$this->statusCode];
+ } else {
+ $this->reasonPhrase = (string) $reason;
+ }
+
+ $this->protocol = $version;
+ }
+
+ public function getStatusCode()
+ {
+ return $this->statusCode;
+ }
+
+ public function getReasonPhrase()
+ {
+ return $this->reasonPhrase;
+ }
+
+ public function withStatus($code, $reasonPhrase = '')
+ {
+ $new = clone $this;
+ $new->statusCode = (int) $code;
+ if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
+ $reasonPhrase = self::$phrases[$new->statusCode];
+ }
+ $new->reasonPhrase = $reasonPhrase;
+ return $new;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/Rfc7230.php b/bin/wiki/vendor/guzzlehttp/psr7/src/Rfc7230.php
new file mode 100644
index 00000000..505e4742
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/Rfc7230.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace GuzzleHttp\Psr7;
+
+final class Rfc7230
+{
+ /**
+ * Header related regular expressions (copied from amphp/http package)
+ * (Note: once we require PHP 7.x we could just depend on the upstream package)
+ *
+ * Note: header delimiter (\r\n) is modified to \r?\n to accept line feed only delimiters for BC reasons.
+ *
+ * @link https://github.com/amphp/http/blob/v1.0.1/src/Rfc7230.php#L12-L15
+ * @license https://github.com/amphp/http/blob/v1.0.1/LICENSE
+ */
+ const HEADER_REGEX = "(^([^()<>@,;:\\\"/[\]?={}\x01-\x20\x7F]++):[ \t]*+((?:[ \t]*+[\x21-\x7E\x80-\xFF]++)*+)[ \t]*+\r?\n)m";
+ const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)";
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/ServerRequest.php b/bin/wiki/vendor/guzzlehttp/psr7/src/ServerRequest.php
new file mode 100644
index 00000000..99f453a5
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/ServerRequest.php
@@ -0,0 +1,376 @@
+<?php
+
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\UriInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+
+/**
+ * Server-side HTTP request
+ *
+ * Extends the Request definition to add methods for accessing incoming data,
+ * specifically server parameters, cookies, matched path parameters, query
+ * string arguments, body parameters, and upload file information.
+ *
+ * "Attributes" are discovered via decomposing the request (and usually
+ * specifically the URI path), and typically will be injected by the application.
+ *
+ * Requests are considered immutable; all methods that might change state are
+ * implemented such that they retain the internal state of the current
+ * message and return a new instance that contains the changed state.
+ */
+class ServerRequest extends Request implements ServerRequestInterface
+{
+ /**
+ * @var array
+ */
+ private $attributes = [];
+
+ /**
+ * @var array
+ */
+ private $cookieParams = [];
+
+ /**
+ * @var null|array|object
+ */
+ private $parsedBody;
+
+ /**
+ * @var array
+ */
+ private $queryParams = [];
+
+ /**
+ * @var array
+ */
+ private $serverParams;
+
+ /**
+ * @var array
+ */
+ private $uploadedFiles = [];
+
+ /**
+ * @param string $method HTTP method
+ * @param string|UriInterface $uri URI
+ * @param array $headers Request headers
+ * @param string|null|resource|StreamInterface $body Request body
+ * @param string $version Protocol version
+ * @param array $serverParams Typically the $_SERVER superglobal
+ */
+ public function __construct(
+ $method,
+ $uri,
+ array $headers = [],
+ $body = null,
+ $version = '1.1',
+ array $serverParams = []
+ ) {
+ $this->serverParams = $serverParams;
+
+ parent::__construct($method, $uri, $headers, $body, $version);
+ }
+
+ /**
+ * Return an UploadedFile instance array.
+ *
+ * @param array $files A array which respect $_FILES structure
+ * @throws InvalidArgumentException for unrecognized values
+ * @return array
+ */
+ public static function normalizeFiles(array $files)
+ {
+ $normalized = [];
+
+ foreach ($files as $key => $value) {
+ if ($value instanceof UploadedFileInterface) {
+ $normalized[$key] = $value;
+ } elseif (is_array($value) && isset($value['tmp_name'])) {
+ $normalized[$key] = self::createUploadedFileFromSpec($value);
+ } elseif (is_array($value)) {
+ $normalized[$key] = self::normalizeFiles($value);
+ continue;
+ } else {
+ throw new InvalidArgumentException('Invalid value in files specification');
+ }
+ }
+
+ return $normalized;
+ }
+
+ /**
+ * Create and return an UploadedFile instance from a $_FILES specification.
+ *
+ * If the specification represents an array of values, this method will
+ * delegate to normalizeNestedFileSpec() and return that return value.
+ *
+ * @param array $value $_FILES struct
+ * @return array|UploadedFileInterface
+ */
+ private static function createUploadedFileFromSpec(array $value)
+ {
+ if (is_array($value['tmp_name'])) {
+ return self::normalizeNestedFileSpec($value);
+ }
+
+ return new UploadedFile(
+ $value['tmp_name'],
+ (int) $value['size'],
+ (int) $value['error'],
+ $value['name'],
+ $value['type']
+ );
+ }
+
+ /**
+ * Normalize an array of file specifications.
+ *
+ * Loops through all nested files and returns a normalized array of
+ * UploadedFileInterface instances.
+ *
+ * @param array $files
+ * @return UploadedFileInterface[]
+ */
+ private static function normalizeNestedFileSpec(array $files = [])
+ {
+ $normalizedFiles = [];
+
+ foreach (array_keys($files['tmp_name']) as $key) {
+ $spec = [
+ 'tmp_name' => $files['tmp_name'][$key],
+ 'size' => $files['size'][$key],
+ 'error' => $files['error'][$key],
+ 'name' => $files['name'][$key],
+ 'type' => $files['type'][$key],
+ ];
+ $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
+ }
+
+ return $normalizedFiles;
+ }
+
+ /**
+ * Return a ServerRequest populated with superglobals:
+ * $_GET
+ * $_POST
+ * $_COOKIE
+ * $_FILES
+ * $_SERVER
+ *
+ * @return ServerRequestInterface
+ */
+ public static function fromGlobals()
+ {
+ $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
+ $headers = getallheaders();
+ $uri = self::getUriFromGlobals();
+ $body = new LazyOpenStream('php://input', 'r+');
+ $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
+
+ $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
+
+ return $serverRequest
+ ->withCookieParams($_COOKIE)
+ ->withQueryParams($_GET)
+ ->withParsedBody($_POST)
+ ->withUploadedFiles(self::normalizeFiles($_FILES));
+ }
+
+ private static function extractHostAndPortFromAuthority($authority)
+ {
+ $uri = 'http://'.$authority;
+ $parts = parse_url($uri);
+ if (false === $parts) {
+ return [null, null];
+ }
+
+ $host = isset($parts['host']) ? $parts['host'] : null;
+ $port = isset($parts['port']) ? $parts['port'] : null;
+
+ return [$host, $port];
+ }
+
+ /**
+ * Get a Uri populated with values from $_SERVER.
+ *
+ * @return UriInterface
+ */
+ public static function getUriFromGlobals()
+ {
+ $uri = new Uri('');
+
+ $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
+
+ $hasPort = false;
+ if (isset($_SERVER['HTTP_HOST'])) {
+ list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
+ if ($host !== null) {
+ $uri = $uri->withHost($host);
+ }
+
+ if ($port !== null) {
+ $hasPort = true;
+ $uri = $uri->withPort($port);
+ }
+ } elseif (isset($_SERVER['SERVER_NAME'])) {
+ $uri = $uri->withHost($_SERVER['SERVER_NAME']);
+ } elseif (isset($_SERVER['SERVER_ADDR'])) {
+ $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
+ }
+
+ if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
+ $uri = $uri->withPort($_SERVER['SERVER_PORT']);
+ }
+
+ $hasQuery = false;
+ if (isset($_SERVER['REQUEST_URI'])) {
+ $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
+ $uri = $uri->withPath($requestUriParts[0]);
+ if (isset($requestUriParts[1])) {
+ $hasQuery = true;
+ $uri = $uri->withQuery($requestUriParts[1]);
+ }
+ }
+
+ if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
+ $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
+ }
+
+ return $uri;
+ }
+
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getServerParams()
+ {
+ return $this->serverParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUploadedFiles()
+ {
+ return $this->uploadedFiles;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withUploadedFiles(array $uploadedFiles)
+ {
+ $new = clone $this;
+ $new->uploadedFiles = $uploadedFiles;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCookieParams()
+ {
+ return $this->cookieParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withCookieParams(array $cookies)
+ {
+ $new = clone $this;
+ $new->cookieParams = $cookies;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQueryParams()
+ {
+ return $this->queryParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withQueryParams(array $query)
+ {
+ $new = clone $this;
+ $new->queryParams = $query;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getParsedBody()
+ {
+ return $this->parsedBody;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withParsedBody($data)
+ {
+ $new = clone $this;
+ $new->parsedBody = $data;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttribute($attribute, $default = null)
+ {
+ if (false === array_key_exists($attribute, $this->attributes)) {
+ return $default;
+ }
+
+ return $this->attributes[$attribute];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withAttribute($attribute, $value)
+ {
+ $new = clone $this;
+ $new->attributes[$attribute] = $value;
+
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withoutAttribute($attribute)
+ {
+ if (false === array_key_exists($attribute, $this->attributes)) {
+ return $this;
+ }
+
+ $new = clone $this;
+ unset($new->attributes[$attribute]);
+
+ return $new;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/Stream.php b/bin/wiki/vendor/guzzlehttp/psr7/src/Stream.php
new file mode 100644
index 00000000..111795eb
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/Stream.php
@@ -0,0 +1,270 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * PHP stream implementation.
+ *
+ * @var $stream
+ */
+class Stream implements StreamInterface
+{
+ private $stream;
+ private $size;
+ private $seekable;
+ private $readable;
+ private $writable;
+ private $uri;
+ private $customMetadata;
+
+ /** @var array Hash of readable and writable stream types */
+ private static $readWriteHash = [
+ 'read' => [
+ 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true,
+ 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true,
+ 'c+b' => true, 'rt' => true, 'w+t' => true, 'r+t' => true,
+ 'x+t' => true, 'c+t' => true, 'a+' => true, 'rb+' => true,
+ ],
+ 'write' => [
+ 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true,
+ 'c+' => true, 'wb' => true, 'w+b' => true, 'r+b' => true, 'rb+' => true,
+ 'x+b' => true, 'c+b' => true, 'w+t' => true, 'r+t' => true,
+ 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true
+ ]
+ ];
+
+ /**
+ * This constructor accepts an associative array of options.
+ *
+ * - size: (int) If a read stream would otherwise have an indeterminate
+ * size, but the size is known due to foreknowledge, then you can
+ * provide that size, in bytes.
+ * - metadata: (array) Any additional metadata to return when the metadata
+ * of the stream is accessed.
+ *
+ * @param resource $stream Stream resource to wrap.
+ * @param array $options Associative array of options.
+ *
+ * @throws \InvalidArgumentException if the stream is not a stream resource
+ */
+ public function __construct($stream, $options = [])
+ {
+ if (!is_resource($stream)) {
+ throw new \InvalidArgumentException('Stream must be a resource');
+ }
+
+ if (isset($options['size'])) {
+ $this->size = $options['size'];
+ }
+
+ $this->customMetadata = isset($options['metadata'])
+ ? $options['metadata']
+ : [];
+
+ $this->stream = $stream;
+ $meta = stream_get_meta_data($this->stream);
+ $this->seekable = $meta['seekable'];
+ $this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
+ $this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
+ $this->uri = $this->getMetadata('uri');
+ }
+
+ /**
+ * Closes the stream when the destructed
+ */
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+ public function __toString()
+ {
+ try {
+ $this->seek(0);
+ return (string) stream_get_contents($this->stream);
+ } catch (\Exception $e) {
+ return '';
+ }
+ }
+
+ public function getContents()
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ $contents = stream_get_contents($this->stream);
+
+ if ($contents === false) {
+ throw new \RuntimeException('Unable to read stream contents');
+ }
+
+ return $contents;
+ }
+
+ public function close()
+ {
+ if (isset($this->stream)) {
+ if (is_resource($this->stream)) {
+ fclose($this->stream);
+ }
+ $this->detach();
+ }
+ }
+
+ public function detach()
+ {
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ $result = $this->stream;
+ unset($this->stream);
+ $this->size = $this->uri = null;
+ $this->readable = $this->writable = $this->seekable = false;
+
+ return $result;
+ }
+
+ public function getSize()
+ {
+ if ($this->size !== null) {
+ return $this->size;
+ }
+
+ if (!isset($this->stream)) {
+ return null;
+ }
+
+ // Clear the stat cache if the stream has a URI
+ if ($this->uri) {
+ clearstatcache(true, $this->uri);
+ }
+
+ $stats = fstat($this->stream);
+ if (isset($stats['size'])) {
+ $this->size = $stats['size'];
+ return $this->size;
+ }
+
+ return null;
+ }
+
+ public function isReadable()
+ {
+ return $this->readable;
+ }
+
+ public function isWritable()
+ {
+ return $this->writable;
+ }
+
+ public function isSeekable()
+ {
+ return $this->seekable;
+ }
+
+ public function eof()
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ return feof($this->stream);
+ }
+
+ public function tell()
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+
+ $result = ftell($this->stream);
+
+ if ($result === false) {
+ throw new \RuntimeException('Unable to determine stream position');
+ }
+
+ return $result;
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->seekable) {
+ throw new \RuntimeException('Stream is not seekable');
+ }
+ if (fseek($this->stream, $offset, $whence) === -1) {
+ throw new \RuntimeException('Unable to seek to stream position '
+ . $offset . ' with whence ' . var_export($whence, true));
+ }
+ }
+
+ public function read($length)
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->readable) {
+ throw new \RuntimeException('Cannot read from non-readable stream');
+ }
+ if ($length < 0) {
+ throw new \RuntimeException('Length parameter cannot be negative');
+ }
+
+ if (0 === $length) {
+ return '';
+ }
+
+ $string = fread($this->stream, $length);
+ if (false === $string) {
+ throw new \RuntimeException('Unable to read from stream');
+ }
+
+ return $string;
+ }
+
+ public function write($string)
+ {
+ if (!isset($this->stream)) {
+ throw new \RuntimeException('Stream is detached');
+ }
+ if (!$this->writable) {
+ throw new \RuntimeException('Cannot write to a non-writable stream');
+ }
+
+ // We can't know the size after writing anything
+ $this->size = null;
+ $result = fwrite($this->stream, $string);
+
+ if ($result === false) {
+ throw new \RuntimeException('Unable to write to stream');
+ }
+
+ return $result;
+ }
+
+ public function getMetadata($key = null)
+ {
+ if (!isset($this->stream)) {
+ return $key ? null : [];
+ } elseif (!$key) {
+ return $this->customMetadata + stream_get_meta_data($this->stream);
+ } elseif (isset($this->customMetadata[$key])) {
+ return $this->customMetadata[$key];
+ }
+
+ $meta = stream_get_meta_data($this->stream);
+
+ return isset($meta[$key]) ? $meta[$key] : null;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php b/bin/wiki/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
new file mode 100644
index 00000000..daec6f52
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/StreamDecoratorTrait.php
@@ -0,0 +1,149 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Stream decorator trait
+ * @property StreamInterface stream
+ */
+trait StreamDecoratorTrait
+{
+ /**
+ * @param StreamInterface $stream Stream to decorate
+ */
+ public function __construct(StreamInterface $stream)
+ {
+ $this->stream = $stream;
+ }
+
+ /**
+ * Magic method used to create a new stream if streams are not added in
+ * the constructor of a decorator (e.g., LazyOpenStream).
+ *
+ * @param string $name Name of the property (allows "stream" only).
+ *
+ * @return StreamInterface
+ */
+ public function __get($name)
+ {
+ if ($name == 'stream') {
+ $this->stream = $this->createStream();
+ return $this->stream;
+ }
+
+ throw new \UnexpectedValueException("$name not found on class");
+ }
+
+ public function __toString()
+ {
+ try {
+ if ($this->isSeekable()) {
+ $this->seek(0);
+ }
+ return $this->getContents();
+ } catch (\Exception $e) {
+ // Really, PHP? https://bugs.php.net/bug.php?id=53648
+ trigger_error('StreamDecorator::__toString exception: '
+ . (string) $e, E_USER_ERROR);
+ return '';
+ }
+ }
+
+ public function getContents()
+ {
+ return copy_to_string($this);
+ }
+
+ /**
+ * Allow decorators to implement custom methods
+ *
+ * @param string $method Missing method name
+ * @param array $args Method arguments
+ *
+ * @return mixed
+ */
+ public function __call($method, array $args)
+ {
+ $result = call_user_func_array([$this->stream, $method], $args);
+
+ // Always return the wrapped object if the result is a return $this
+ return $result === $this->stream ? $this : $result;
+ }
+
+ public function close()
+ {
+ $this->stream->close();
+ }
+
+ public function getMetadata($key = null)
+ {
+ return $this->stream->getMetadata($key);
+ }
+
+ public function detach()
+ {
+ return $this->stream->detach();
+ }
+
+ public function getSize()
+ {
+ return $this->stream->getSize();
+ }
+
+ public function eof()
+ {
+ return $this->stream->eof();
+ }
+
+ public function tell()
+ {
+ return $this->stream->tell();
+ }
+
+ public function isReadable()
+ {
+ return $this->stream->isReadable();
+ }
+
+ public function isWritable()
+ {
+ return $this->stream->isWritable();
+ }
+
+ public function isSeekable()
+ {
+ return $this->stream->isSeekable();
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ $this->stream->seek($offset, $whence);
+ }
+
+ public function read($length)
+ {
+ return $this->stream->read($length);
+ }
+
+ public function write($string)
+ {
+ return $this->stream->write($string);
+ }
+
+ /**
+ * Implement in subclasses to dynamically create streams when requested.
+ *
+ * @return StreamInterface
+ * @throws \BadMethodCallException
+ */
+ protected function createStream()
+ {
+ throw new \BadMethodCallException('Not implemented');
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/StreamWrapper.php b/bin/wiki/vendor/guzzlehttp/psr7/src/StreamWrapper.php
new file mode 100644
index 00000000..0f3a2856
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/StreamWrapper.php
@@ -0,0 +1,161 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * Converts Guzzle streams into PHP stream resources.
+ */
+class StreamWrapper
+{
+ /** @var resource */
+ public $context;
+
+ /** @var StreamInterface */
+ private $stream;
+
+ /** @var string r, r+, or w */
+ private $mode;
+
+ /**
+ * Returns a resource representing the stream.
+ *
+ * @param StreamInterface $stream The stream to get a resource for
+ *
+ * @return resource
+ * @throws \InvalidArgumentException if stream is not readable or writable
+ */
+ public static function getResource(StreamInterface $stream)
+ {
+ self::register();
+
+ if ($stream->isReadable()) {
+ $mode = $stream->isWritable() ? 'r+' : 'r';
+ } elseif ($stream->isWritable()) {
+ $mode = 'w';
+ } else {
+ throw new \InvalidArgumentException('The stream must be readable, '
+ . 'writable, or both.');
+ }
+
+ return fopen('guzzle://stream', $mode, null, self::createStreamContext($stream));
+ }
+
+ /**
+ * Creates a stream context that can be used to open a stream as a php stream resource.
+ *
+ * @param StreamInterface $stream
+ *
+ * @return resource
+ */
+ public static function createStreamContext(StreamInterface $stream)
+ {
+ return stream_context_create([
+ 'guzzle' => ['stream' => $stream]
+ ]);
+ }
+
+ /**
+ * Registers the stream wrapper if needed
+ */
+ public static function register()
+ {
+ if (!in_array('guzzle', stream_get_wrappers())) {
+ stream_wrapper_register('guzzle', __CLASS__);
+ }
+ }
+
+ public function stream_open($path, $mode, $options, &$opened_path)
+ {
+ $options = stream_context_get_options($this->context);
+
+ if (!isset($options['guzzle']['stream'])) {
+ return false;
+ }
+
+ $this->mode = $mode;
+ $this->stream = $options['guzzle']['stream'];
+
+ return true;
+ }
+
+ public function stream_read($count)
+ {
+ return $this->stream->read($count);
+ }
+
+ public function stream_write($data)
+ {
+ return (int) $this->stream->write($data);
+ }
+
+ public function stream_tell()
+ {
+ return $this->stream->tell();
+ }
+
+ public function stream_eof()
+ {
+ return $this->stream->eof();
+ }
+
+ public function stream_seek($offset, $whence)
+ {
+ $this->stream->seek($offset, $whence);
+
+ return true;
+ }
+
+ public function stream_cast($cast_as)
+ {
+ $stream = clone($this->stream);
+
+ return $stream->detach();
+ }
+
+ public function stream_stat()
+ {
+ static $modeMap = [
+ 'r' => 33060,
+ 'rb' => 33060,
+ 'r+' => 33206,
+ 'w' => 33188,
+ 'wb' => 33188
+ ];
+
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => $modeMap[$this->mode],
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => $this->stream->getSize() ?: 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0
+ ];
+ }
+
+ public function url_stat($path, $flags)
+ {
+ return [
+ 'dev' => 0,
+ 'ino' => 0,
+ 'mode' => 0,
+ 'nlink' => 0,
+ 'uid' => 0,
+ 'gid' => 0,
+ 'rdev' => 0,
+ 'size' => 0,
+ 'atime' => 0,
+ 'mtime' => 0,
+ 'ctime' => 0,
+ 'blksize' => 0,
+ 'blocks' => 0
+ ];
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/UploadedFile.php b/bin/wiki/vendor/guzzlehttp/psr7/src/UploadedFile.php
new file mode 100644
index 00000000..e62bd5c8
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/UploadedFile.php
@@ -0,0 +1,316 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use InvalidArgumentException;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use RuntimeException;
+
+class UploadedFile implements UploadedFileInterface
+{
+ /**
+ * @var int[]
+ */
+ private static $errors = [
+ UPLOAD_ERR_OK,
+ UPLOAD_ERR_INI_SIZE,
+ UPLOAD_ERR_FORM_SIZE,
+ UPLOAD_ERR_PARTIAL,
+ UPLOAD_ERR_NO_FILE,
+ UPLOAD_ERR_NO_TMP_DIR,
+ UPLOAD_ERR_CANT_WRITE,
+ UPLOAD_ERR_EXTENSION,
+ ];
+
+ /**
+ * @var string
+ */
+ private $clientFilename;
+
+ /**
+ * @var string
+ */
+ private $clientMediaType;
+
+ /**
+ * @var int
+ */
+ private $error;
+
+ /**
+ * @var null|string
+ */
+ private $file;
+
+ /**
+ * @var bool
+ */
+ private $moved = false;
+
+ /**
+ * @var int
+ */
+ private $size;
+
+ /**
+ * @var StreamInterface|null
+ */
+ private $stream;
+
+ /**
+ * @param StreamInterface|string|resource $streamOrFile
+ * @param int $size
+ * @param int $errorStatus
+ * @param string|null $clientFilename
+ * @param string|null $clientMediaType
+ */
+ public function __construct(
+ $streamOrFile,
+ $size,
+ $errorStatus,
+ $clientFilename = null,
+ $clientMediaType = null
+ ) {
+ $this->setError($errorStatus);
+ $this->setSize($size);
+ $this->setClientFilename($clientFilename);
+ $this->setClientMediaType($clientMediaType);
+
+ if ($this->isOk()) {
+ $this->setStreamOrFile($streamOrFile);
+ }
+ }
+
+ /**
+ * Depending on the value set file or stream variable
+ *
+ * @param mixed $streamOrFile
+ * @throws InvalidArgumentException
+ */
+ private function setStreamOrFile($streamOrFile)
+ {
+ if (is_string($streamOrFile)) {
+ $this->file = $streamOrFile;
+ } elseif (is_resource($streamOrFile)) {
+ $this->stream = new Stream($streamOrFile);
+ } elseif ($streamOrFile instanceof StreamInterface) {
+ $this->stream = $streamOrFile;
+ } else {
+ throw new InvalidArgumentException(
+ 'Invalid stream or file provided for UploadedFile'
+ );
+ }
+ }
+
+ /**
+ * @param int $error
+ * @throws InvalidArgumentException
+ */
+ private function setError($error)
+ {
+ if (false === is_int($error)) {
+ throw new InvalidArgumentException(
+ 'Upload file error status must be an integer'
+ );
+ }
+
+ if (false === in_array($error, UploadedFile::$errors)) {
+ throw new InvalidArgumentException(
+ 'Invalid error status for UploadedFile'
+ );
+ }
+
+ $this->error = $error;
+ }
+
+ /**
+ * @param int $size
+ * @throws InvalidArgumentException
+ */
+ private function setSize($size)
+ {
+ if (false === is_int($size)) {
+ throw new InvalidArgumentException(
+ 'Upload file size must be an integer'
+ );
+ }
+
+ $this->size = $size;
+ }
+
+ /**
+ * @param mixed $param
+ * @return boolean
+ */
+ private function isStringOrNull($param)
+ {
+ return in_array(gettype($param), ['string', 'NULL']);
+ }
+
+ /**
+ * @param mixed $param
+ * @return boolean
+ */
+ private function isStringNotEmpty($param)
+ {
+ return is_string($param) && false === empty($param);
+ }
+
+ /**
+ * @param string|null $clientFilename
+ * @throws InvalidArgumentException
+ */
+ private function setClientFilename($clientFilename)
+ {
+ if (false === $this->isStringOrNull($clientFilename)) {
+ throw new InvalidArgumentException(
+ 'Upload file client filename must be a string or null'
+ );
+ }
+
+ $this->clientFilename = $clientFilename;
+ }
+
+ /**
+ * @param string|null $clientMediaType
+ * @throws InvalidArgumentException
+ */
+ private function setClientMediaType($clientMediaType)
+ {
+ if (false === $this->isStringOrNull($clientMediaType)) {
+ throw new InvalidArgumentException(
+ 'Upload file client media type must be a string or null'
+ );
+ }
+
+ $this->clientMediaType = $clientMediaType;
+ }
+
+ /**
+ * Return true if there is no upload error
+ *
+ * @return boolean
+ */
+ private function isOk()
+ {
+ return $this->error === UPLOAD_ERR_OK;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isMoved()
+ {
+ return $this->moved;
+ }
+
+ /**
+ * @throws RuntimeException if is moved or not ok
+ */
+ private function validateActive()
+ {
+ if (false === $this->isOk()) {
+ throw new RuntimeException('Cannot retrieve stream due to upload error');
+ }
+
+ if ($this->isMoved()) {
+ throw new RuntimeException('Cannot retrieve stream after it has already been moved');
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ * @throws RuntimeException if the upload was not successful.
+ */
+ public function getStream()
+ {
+ $this->validateActive();
+
+ if ($this->stream instanceof StreamInterface) {
+ return $this->stream;
+ }
+
+ return new LazyOpenStream($this->file, 'r+');
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see http://php.net/is_uploaded_file
+ * @see http://php.net/move_uploaded_file
+ * @param string $targetPath Path to which to move the uploaded file.
+ * @throws RuntimeException if the upload was not successful.
+ * @throws InvalidArgumentException if the $path specified is invalid.
+ * @throws RuntimeException on any error during the move operation, or on
+ * the second or subsequent call to the method.
+ */
+ public function moveTo($targetPath)
+ {
+ $this->validateActive();
+
+ if (false === $this->isStringNotEmpty($targetPath)) {
+ throw new InvalidArgumentException(
+ 'Invalid path provided for move operation; must be a non-empty string'
+ );
+ }
+
+ if ($this->file) {
+ $this->moved = php_sapi_name() == 'cli'
+ ? rename($this->file, $targetPath)
+ : move_uploaded_file($this->file, $targetPath);
+ } else {
+ copy_to_stream(
+ $this->getStream(),
+ new LazyOpenStream($targetPath, 'w')
+ );
+
+ $this->moved = true;
+ }
+
+ if (false === $this->moved) {
+ throw new RuntimeException(
+ sprintf('Uploaded file could not be moved to %s', $targetPath)
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return int|null The file size in bytes or null if unknown.
+ */
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see http://php.net/manual/en/features.file-upload.errors.php
+ * @return int One of PHP's UPLOAD_ERR_XXX constants.
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return string|null The filename sent by the client or null if none
+ * was provided.
+ */
+ public function getClientFilename()
+ {
+ return $this->clientFilename;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getClientMediaType()
+ {
+ return $this->clientMediaType;
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/Uri.php b/bin/wiki/vendor/guzzlehttp/psr7/src/Uri.php
new file mode 100644
index 00000000..36219568
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/Uri.php
@@ -0,0 +1,738 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * PSR-7 URI implementation.
+ *
+ * @author Michael Dowling
+ * @author Tobias Schultze
+ * @author Matthew Weier O'Phinney
+ */
+class Uri implements UriInterface
+{
+ /**
+ * Absolute http and https URIs require a host per RFC 7230 Section 2.7
+ * but in generic URIs the host can be empty. So for http(s) URIs
+ * we apply this default host when no host is given yet to form a
+ * valid URI.
+ */
+ const HTTP_DEFAULT_HOST = 'localhost';
+
+ private static $defaultPorts = [
+ 'http' => 80,
+ 'https' => 443,
+ 'ftp' => 21,
+ 'gopher' => 70,
+ 'nntp' => 119,
+ 'news' => 119,
+ 'telnet' => 23,
+ 'tn3270' => 23,
+ 'imap' => 143,
+ 'pop' => 110,
+ 'ldap' => 389,
+ ];
+
+ private static $charUnreserved = 'a-zA-Z0-9_\-\.~';
+ private static $charSubDelims = '!\$&\'\(\)\*\+,;=';
+ private static $replaceQuery = ['=' => '%3D', '&' => '%26'];
+
+ /** @var string Uri scheme. */
+ private $scheme = '';
+
+ /** @var string Uri user info. */
+ private $userInfo = '';
+
+ /** @var string Uri host. */
+ private $host = '';
+
+ /** @var int|null Uri port. */
+ private $port;
+
+ /** @var string Uri path. */
+ private $path = '';
+
+ /** @var string Uri query string. */
+ private $query = '';
+
+ /** @var string Uri fragment. */
+ private $fragment = '';
+
+ /**
+ * @param string $uri URI to parse
+ */
+ public function __construct($uri = '')
+ {
+ // weak type check to also accept null until we can add scalar type hints
+ if ($uri != '') {
+ $parts = parse_url($uri);
+ if ($parts === false) {
+ throw new \InvalidArgumentException("Unable to parse URI: $uri");
+ }
+ $this->applyParts($parts);
+ }
+ }
+
+ public function __toString()
+ {
+ return self::composeComponents(
+ $this->scheme,
+ $this->getAuthority(),
+ $this->path,
+ $this->query,
+ $this->fragment
+ );
+ }
+
+ /**
+ * Composes a URI reference string from its various components.
+ *
+ * Usually this method does not need to be called manually but instead is used indirectly via
+ * `Psr\Http\Message\UriInterface::__toString`.
+ *
+ * PSR-7 UriInterface treats an empty component the same as a missing component as
+ * getQuery(), getFragment() etc. always return a string. This explains the slight
+ * difference to RFC 3986 Section 5.3.
+ *
+ * Another adjustment is that the authority separator is added even when the authority is missing/empty
+ * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with
+ * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But
+ * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to
+ * that format).
+ *
+ * @param string $scheme
+ * @param string $authority
+ * @param string $path
+ * @param string $query
+ * @param string $fragment
+ *
+ * @return string
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-5.3
+ */
+ public static function composeComponents($scheme, $authority, $path, $query, $fragment)
+ {
+ $uri = '';
+
+ // weak type checks to also accept null until we can add scalar type hints
+ if ($scheme != '') {
+ $uri .= $scheme . ':';
+ }
+
+ if ($authority != ''|| $scheme === 'file') {
+ $uri .= '//' . $authority;
+ }
+
+ $uri .= $path;
+
+ if ($query != '') {
+ $uri .= '?' . $query;
+ }
+
+ if ($fragment != '') {
+ $uri .= '#' . $fragment;
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Whether the URI has the default port of the current scheme.
+ *
+ * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used
+ * independently of the implementation.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ */
+ public static function isDefaultPort(UriInterface $uri)
+ {
+ return $uri->getPort() === null
+ || (isset(self::$defaultPorts[$uri->getScheme()]) && $uri->getPort() === self::$defaultPorts[$uri->getScheme()]);
+ }
+
+ /**
+ * Whether the URI is absolute, i.e. it has a scheme.
+ *
+ * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
+ * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
+ * to another URI, the base URI. Relative references can be divided into several forms:
+ * - network-path references, e.g. '//example.com/path'
+ * - absolute-path references, e.g. '/path'
+ * - relative-path references, e.g. 'subpath'
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @see Uri::isNetworkPathReference
+ * @see Uri::isAbsolutePathReference
+ * @see Uri::isRelativePathReference
+ * @link https://tools.ietf.org/html/rfc3986#section-4
+ */
+ public static function isAbsolute(UriInterface $uri)
+ {
+ return $uri->getScheme() !== '';
+ }
+
+ /**
+ * Whether the URI is a network-path reference.
+ *
+ * A relative reference that begins with two slash characters is termed an network-path reference.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ public static function isNetworkPathReference(UriInterface $uri)
+ {
+ return $uri->getScheme() === '' && $uri->getAuthority() !== '';
+ }
+
+ /**
+ * Whether the URI is a absolute-path reference.
+ *
+ * A relative reference that begins with a single slash character is termed an absolute-path reference.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ public static function isAbsolutePathReference(UriInterface $uri)
+ {
+ return $uri->getScheme() === ''
+ && $uri->getAuthority() === ''
+ && isset($uri->getPath()[0])
+ && $uri->getPath()[0] === '/';
+ }
+
+ /**
+ * Whether the URI is a relative-path reference.
+ *
+ * A relative reference that does not begin with a slash character is termed a relative-path reference.
+ *
+ * @param UriInterface $uri
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.2
+ */
+ public static function isRelativePathReference(UriInterface $uri)
+ {
+ return $uri->getScheme() === ''
+ && $uri->getAuthority() === ''
+ && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
+ }
+
+ /**
+ * Whether the URI is a same-document reference.
+ *
+ * A same-document reference refers to a URI that is, aside from its fragment
+ * component, identical to the base URI. When no base URI is given, only an empty
+ * URI reference (apart from its fragment) is considered a same-document reference.
+ *
+ * @param UriInterface $uri The URI to check
+ * @param UriInterface|null $base An optional base URI to compare against
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-4.4
+ */
+ public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null)
+ {
+ if ($base !== null) {
+ $uri = UriResolver::resolve($base, $uri);
+
+ return ($uri->getScheme() === $base->getScheme())
+ && ($uri->getAuthority() === $base->getAuthority())
+ && ($uri->getPath() === $base->getPath())
+ && ($uri->getQuery() === $base->getQuery());
+ }
+
+ return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === '';
+ }
+
+ /**
+ * Removes dot segments from a path and returns the new path.
+ *
+ * @param string $path
+ *
+ * @return string
+ *
+ * @deprecated since version 1.4. Use UriResolver::removeDotSegments instead.
+ * @see UriResolver::removeDotSegments
+ */
+ public static function removeDotSegments($path)
+ {
+ return UriResolver::removeDotSegments($path);
+ }
+
+ /**
+ * Converts the relative URI into a new URI that is resolved against the base URI.
+ *
+ * @param UriInterface $base Base URI
+ * @param string|UriInterface $rel Relative URI
+ *
+ * @return UriInterface
+ *
+ * @deprecated since version 1.4. Use UriResolver::resolve instead.
+ * @see UriResolver::resolve
+ */
+ public static function resolve(UriInterface $base, $rel)
+ {
+ if (!($rel instanceof UriInterface)) {
+ $rel = new self($rel);
+ }
+
+ return UriResolver::resolve($base, $rel);
+ }
+
+ /**
+ * Creates a new URI with a specific query string value removed.
+ *
+ * Any existing query string values that exactly match the provided key are
+ * removed.
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param string $key Query string key to remove.
+ *
+ * @return UriInterface
+ */
+ public static function withoutQueryValue(UriInterface $uri, $key)
+ {
+ $result = self::getFilteredQueryString($uri, [$key]);
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a new URI with a specific query string value.
+ *
+ * Any existing query string values that exactly match the provided key are
+ * removed and replaced with the given key value pair.
+ *
+ * A value of null will set the query string key without a value, e.g. "key"
+ * instead of "key=value".
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param string $key Key to set.
+ * @param string|null $value Value to set
+ *
+ * @return UriInterface
+ */
+ public static function withQueryValue(UriInterface $uri, $key, $value)
+ {
+ $result = self::getFilteredQueryString($uri, [$key]);
+
+ $result[] = self::generateQueryString($key, $value);
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a new URI with multiple specific query string values.
+ *
+ * It has the same behavior as withQueryValue() but for an associative array of key => value.
+ *
+ * @param UriInterface $uri URI to use as a base.
+ * @param array $keyValueArray Associative array of key and values
+ *
+ * @return UriInterface
+ */
+ public static function withQueryValues(UriInterface $uri, array $keyValueArray)
+ {
+ $result = self::getFilteredQueryString($uri, array_keys($keyValueArray));
+
+ foreach ($keyValueArray as $key => $value) {
+ $result[] = self::generateQueryString($key, $value);
+ }
+
+ return $uri->withQuery(implode('&', $result));
+ }
+
+ /**
+ * Creates a URI from a hash of `parse_url` components.
+ *
+ * @param array $parts
+ *
+ * @return UriInterface
+ * @link http://php.net/manual/en/function.parse-url.php
+ *
+ * @throws \InvalidArgumentException If the components do not form a valid URI.
+ */
+ public static function fromParts(array $parts)
+ {
+ $uri = new self();
+ $uri->applyParts($parts);
+ $uri->validateState();
+
+ return $uri;
+ }
+
+ public function getScheme()
+ {
+ return $this->scheme;
+ }
+
+ public function getAuthority()
+ {
+ $authority = $this->host;
+ if ($this->userInfo !== '') {
+ $authority = $this->userInfo . '@' . $authority;
+ }
+
+ if ($this->port !== null) {
+ $authority .= ':' . $this->port;
+ }
+
+ return $authority;
+ }
+
+ public function getUserInfo()
+ {
+ return $this->userInfo;
+ }
+
+ public function getHost()
+ {
+ return $this->host;
+ }
+
+ public function getPort()
+ {
+ return $this->port;
+ }
+
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ public function getFragment()
+ {
+ return $this->fragment;
+ }
+
+ public function withScheme($scheme)
+ {
+ $scheme = $this->filterScheme($scheme);
+
+ if ($this->scheme === $scheme) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->scheme = $scheme;
+ $new->removeDefaultPort();
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withUserInfo($user, $password = null)
+ {
+ $info = $user;
+ if ($password != '') {
+ $info .= ':' . $password;
+ }
+
+ if ($this->userInfo === $info) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->userInfo = $info;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withHost($host)
+ {
+ $host = $this->filterHost($host);
+
+ if ($this->host === $host) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->host = $host;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withPort($port)
+ {
+ $port = $this->filterPort($port);
+
+ if ($this->port === $port) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->port = $port;
+ $new->removeDefaultPort();
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withPath($path)
+ {
+ $path = $this->filterPath($path);
+
+ if ($this->path === $path) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->path = $path;
+ $new->validateState();
+
+ return $new;
+ }
+
+ public function withQuery($query)
+ {
+ $query = $this->filterQueryAndFragment($query);
+
+ if ($this->query === $query) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->query = $query;
+
+ return $new;
+ }
+
+ public function withFragment($fragment)
+ {
+ $fragment = $this->filterQueryAndFragment($fragment);
+
+ if ($this->fragment === $fragment) {
+ return $this;
+ }
+
+ $new = clone $this;
+ $new->fragment = $fragment;
+
+ return $new;
+ }
+
+ /**
+ * Apply parse_url parts to a URI.
+ *
+ * @param array $parts Array of parse_url parts to apply.
+ */
+ private function applyParts(array $parts)
+ {
+ $this->scheme = isset($parts['scheme'])
+ ? $this->filterScheme($parts['scheme'])
+ : '';
+ $this->userInfo = isset($parts['user']) ? $parts['user'] : '';
+ $this->host = isset($parts['host'])
+ ? $this->filterHost($parts['host'])
+ : '';
+ $this->port = isset($parts['port'])
+ ? $this->filterPort($parts['port'])
+ : null;
+ $this->path = isset($parts['path'])
+ ? $this->filterPath($parts['path'])
+ : '';
+ $this->query = isset($parts['query'])
+ ? $this->filterQueryAndFragment($parts['query'])
+ : '';
+ $this->fragment = isset($parts['fragment'])
+ ? $this->filterQueryAndFragment($parts['fragment'])
+ : '';
+ if (isset($parts['pass'])) {
+ $this->userInfo .= ':' . $parts['pass'];
+ }
+
+ $this->removeDefaultPort();
+ }
+
+ /**
+ * @param string $scheme
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the scheme is invalid.
+ */
+ private function filterScheme($scheme)
+ {
+ if (!is_string($scheme)) {
+ throw new \InvalidArgumentException('Scheme must be a string');
+ }
+
+ return strtolower($scheme);
+ }
+
+ /**
+ * @param string $host
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the host is invalid.
+ */
+ private function filterHost($host)
+ {
+ if (!is_string($host)) {
+ throw new \InvalidArgumentException('Host must be a string');
+ }
+
+ return strtolower($host);
+ }
+
+ /**
+ * @param int|null $port
+ *
+ * @return int|null
+ *
+ * @throws \InvalidArgumentException If the port is invalid.
+ */
+ private function filterPort($port)
+ {
+ if ($port === null) {
+ return null;
+ }
+
+ $port = (int) $port;
+ if (1 > $port || 0xffff < $port) {
+ throw new \InvalidArgumentException(
+ sprintf('Invalid port: %d. Must be between 1 and 65535', $port)
+ );
+ }
+
+ return $port;
+ }
+
+ /**
+ * @param UriInterface $uri
+ * @param array $keys
+ *
+ * @return array
+ */
+ private static function getFilteredQueryString(UriInterface $uri, array $keys)
+ {
+ $current = $uri->getQuery();
+
+ if ($current === '') {
+ return [];
+ }
+
+ $decodedKeys = array_map('rawurldecode', $keys);
+
+ return array_filter(explode('&', $current), function ($part) use ($decodedKeys) {
+ return !in_array(rawurldecode(explode('=', $part)[0]), $decodedKeys, true);
+ });
+ }
+
+ /**
+ * @param string $key
+ * @param string|null $value
+ *
+ * @return string
+ */
+ private static function generateQueryString($key, $value)
+ {
+ // Query string separators ("=", "&") within the key or value need to be encoded
+ // (while preventing double-encoding) before setting the query string. All other
+ // chars that need percent-encoding will be encoded by withQuery().
+ $queryString = strtr($key, self::$replaceQuery);
+
+ if ($value !== null) {
+ $queryString .= '=' . strtr($value, self::$replaceQuery);
+ }
+
+ return $queryString;
+ }
+
+ private function removeDefaultPort()
+ {
+ if ($this->port !== null && self::isDefaultPort($this)) {
+ $this->port = null;
+ }
+ }
+
+ /**
+ * Filters the path of a URI
+ *
+ * @param string $path
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the path is invalid.
+ */
+ private function filterPath($path)
+ {
+ if (!is_string($path)) {
+ throw new \InvalidArgumentException('Path must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/]++|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $path
+ );
+ }
+
+ /**
+ * Filters the query string or fragment of a URI.
+ *
+ * @param string $str
+ *
+ * @return string
+ *
+ * @throws \InvalidArgumentException If the query or fragment is invalid.
+ */
+ private function filterQueryAndFragment($str)
+ {
+ if (!is_string($str)) {
+ throw new \InvalidArgumentException('Query and fragment must be a string');
+ }
+
+ return preg_replace_callback(
+ '/(?:[^' . self::$charUnreserved . self::$charSubDelims . '%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/',
+ [$this, 'rawurlencodeMatchZero'],
+ $str
+ );
+ }
+
+ private function rawurlencodeMatchZero(array $match)
+ {
+ return rawurlencode($match[0]);
+ }
+
+ private function validateState()
+ {
+ if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) {
+ $this->host = self::HTTP_DEFAULT_HOST;
+ }
+
+ if ($this->getAuthority() === '') {
+ if (0 === strpos($this->path, '//')) {
+ throw new \InvalidArgumentException('The path of a URI without an authority must not start with two slashes "//"');
+ }
+ if ($this->scheme === '' && false !== strpos(explode('/', $this->path, 2)[0], ':')) {
+ throw new \InvalidArgumentException('A relative URI must not have a path beginning with a segment containing a colon');
+ }
+ } elseif (isset($this->path[0]) && $this->path[0] !== '/') {
+ @trigger_error(
+ 'The path of a URI with an authority must start with a slash "/" or be empty. Automagically fixing the URI ' .
+ 'by adding a leading slash to the path is deprecated since version 1.4 and will throw an exception instead.',
+ E_USER_DEPRECATED
+ );
+ $this->path = '/'. $this->path;
+ //throw new \InvalidArgumentException('The path of a URI with an authority must start with a slash "/" or be empty');
+ }
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/bin/wiki/vendor/guzzlehttp/psr7/src/UriNormalizer.php
new file mode 100644
index 00000000..384c29e5
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/UriNormalizer.php
@@ -0,0 +1,216 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Provides methods to normalize and compare URIs.
+ *
+ * @author Tobias Schultze
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-6
+ */
+final class UriNormalizer
+{
+ /**
+ * Default normalizations which only include the ones that preserve semantics.
+ *
+ * self::CAPITALIZE_PERCENT_ENCODING | self::DECODE_UNRESERVED_CHARACTERS | self::CONVERT_EMPTY_PATH |
+ * self::REMOVE_DEFAULT_HOST | self::REMOVE_DEFAULT_PORT | self::REMOVE_DOT_SEGMENTS
+ */
+ const PRESERVING_NORMALIZATIONS = 63;
+
+ /**
+ * All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
+ *
+ * Example: http://example.org/a%c2%b1b → http://example.org/a%C2%B1b
+ */
+ const CAPITALIZE_PERCENT_ENCODING = 1;
+
+ /**
+ * Decodes percent-encoded octets of unreserved characters.
+ *
+ * For consistency, percent-encoded octets in the ranges of ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39),
+ * hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should not be created by URI producers and,
+ * when found in a URI, should be decoded to their corresponding unreserved characters by URI normalizers.
+ *
+ * Example: http://example.org/%7Eusern%61me/ → http://example.org/~username/
+ */
+ const DECODE_UNRESERVED_CHARACTERS = 2;
+
+ /**
+ * Converts the empty path to "/" for http and https URIs.
+ *
+ * Example: http://example.org → http://example.org/
+ */
+ const CONVERT_EMPTY_PATH = 4;
+
+ /**
+ * Removes the default host of the given URI scheme from the URI.
+ *
+ * Only the "file" scheme defines the default host "localhost".
+ * All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile`
+ * are equivalent according to RFC 3986. The first format is not accepted
+ * by PHPs stream functions and thus already normalized implicitly to the
+ * second format in the Uri class. See `GuzzleHttp\Psr7\Uri::composeComponents`.
+ *
+ * Example: file://localhost/myfile → file:///myfile
+ */
+ const REMOVE_DEFAULT_HOST = 8;
+
+ /**
+ * Removes the default port of the given URI scheme from the URI.
+ *
+ * Example: http://example.org:80/ → http://example.org/
+ */
+ const REMOVE_DEFAULT_PORT = 16;
+
+ /**
+ * Removes unnecessary dot-segments.
+ *
+ * Dot-segments in relative-path references are not removed as it would
+ * change the semantics of the URI reference.
+ *
+ * Example: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html
+ */
+ const REMOVE_DOT_SEGMENTS = 32;
+
+ /**
+ * Paths which include two or more adjacent slashes are converted to one.
+ *
+ * Webservers usually ignore duplicate slashes and treat those URIs equivalent.
+ * But in theory those URIs do not need to be equivalent. So this normalization
+ * may change the semantics. Encoded slashes (%2F) are not removed.
+ *
+ * Example: http://example.org//foo///bar.html → http://example.org/foo/bar.html
+ */
+ const REMOVE_DUPLICATE_SLASHES = 64;
+
+ /**
+ * Sort query parameters with their values in alphabetical order.
+ *
+ * However, the order of parameters in a URI may be significant (this is not defined by the standard).
+ * So this normalization is not safe and may change the semantics of the URI.
+ *
+ * Example: ?lang=en&article=fred → ?article=fred&lang=en
+ *
+ * Note: The sorting is neither locale nor Unicode aware (the URI query does not get decoded at all) as the
+ * purpose is to be able to compare URIs in a reproducible way, not to have the params sorted perfectly.
+ */
+ const SORT_QUERY_PARAMETERS = 128;
+
+ /**
+ * Returns a normalized URI.
+ *
+ * The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
+ * This methods adds additional normalizations that can be configured with the $flags parameter.
+ *
+ * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as
+ * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are
+ * treated equivalent which is not necessarily true according to RFC 3986. But that difference
+ * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well.
+ *
+ * @param UriInterface $uri The URI to normalize
+ * @param int $flags A bitmask of normalizations to apply, see constants
+ *
+ * @return UriInterface The normalized URI
+ * @link https://tools.ietf.org/html/rfc3986#section-6.2
+ */
+ public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS)
+ {
+ if ($flags & self::CAPITALIZE_PERCENT_ENCODING) {
+ $uri = self::capitalizePercentEncoding($uri);
+ }
+
+ if ($flags & self::DECODE_UNRESERVED_CHARACTERS) {
+ $uri = self::decodeUnreservedCharacters($uri);
+ }
+
+ if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' &&
+ ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')
+ ) {
+ $uri = $uri->withPath('/');
+ }
+
+ if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') {
+ $uri = $uri->withHost('');
+ }
+
+ if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) {
+ $uri = $uri->withPort(null);
+ }
+
+ if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) {
+ $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath()));
+ }
+
+ if ($flags & self::REMOVE_DUPLICATE_SLASHES) {
+ $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath()));
+ }
+
+ if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') {
+ $queryKeyValues = explode('&', $uri->getQuery());
+ sort($queryKeyValues);
+ $uri = $uri->withQuery(implode('&', $queryKeyValues));
+ }
+
+ return $uri;
+ }
+
+ /**
+ * Whether two URIs can be considered equivalent.
+ *
+ * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also
+ * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be
+ * resolved against the same base URI. If this is not the case, determination of equivalence or difference of
+ * relative references does not mean anything.
+ *
+ * @param UriInterface $uri1 An URI to compare
+ * @param UriInterface $uri2 An URI to compare
+ * @param int $normalizations A bitmask of normalizations to apply, see constants
+ *
+ * @return bool
+ * @link https://tools.ietf.org/html/rfc3986#section-6.1
+ */
+ public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS)
+ {
+ return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations);
+ }
+
+ private static function capitalizePercentEncoding(UriInterface $uri)
+ {
+ $regex = '/(?:%[A-Fa-f0-9]{2})++/';
+
+ $callback = function (array $match) {
+ return strtoupper($match[0]);
+ };
+
+ return
+ $uri->withPath(
+ preg_replace_callback($regex, $callback, $uri->getPath())
+ )->withQuery(
+ preg_replace_callback($regex, $callback, $uri->getQuery())
+ );
+ }
+
+ private static function decodeUnreservedCharacters(UriInterface $uri)
+ {
+ $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i';
+
+ $callback = function (array $match) {
+ return rawurldecode($match[0]);
+ };
+
+ return
+ $uri->withPath(
+ preg_replace_callback($regex, $callback, $uri->getPath())
+ )->withQuery(
+ preg_replace_callback($regex, $callback, $uri->getQuery())
+ );
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/UriResolver.php b/bin/wiki/vendor/guzzlehttp/psr7/src/UriResolver.php
new file mode 100644
index 00000000..c1cb8a27
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/UriResolver.php
@@ -0,0 +1,219 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Resolves a URI reference in the context of a base URI and the opposite way.
+ *
+ * @author Tobias Schultze
+ *
+ * @link https://tools.ietf.org/html/rfc3986#section-5
+ */
+final class UriResolver
+{
+ /**
+ * Removes dot segments from a path and returns the new path.
+ *
+ * @param string $path
+ *
+ * @return string
+ * @link http://tools.ietf.org/html/rfc3986#section-5.2.4
+ */
+ public static function removeDotSegments($path)
+ {
+ if ($path === '' || $path === '/') {
+ return $path;
+ }
+
+ $results = [];
+ $segments = explode('/', $path);
+ foreach ($segments as $segment) {
+ if ($segment === '..') {
+ array_pop($results);
+ } elseif ($segment !== '.') {
+ $results[] = $segment;
+ }
+ }
+
+ $newPath = implode('/', $results);
+
+ if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
+ // Re-add the leading slash if necessary for cases like "/.."
+ $newPath = '/' . $newPath;
+ } elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
+ // Add the trailing slash if necessary
+ // If newPath is not empty, then $segment must be set and is the last segment from the foreach
+ $newPath .= '/';
+ }
+
+ return $newPath;
+ }
+
+ /**
+ * Converts the relative URI into a new URI that is resolved against the base URI.
+ *
+ * @param UriInterface $base Base URI
+ * @param UriInterface $rel Relative URI
+ *
+ * @return UriInterface
+ * @link http://tools.ietf.org/html/rfc3986#section-5.2
+ */
+ public static function resolve(UriInterface $base, UriInterface $rel)
+ {
+ if ((string) $rel === '') {
+ // we can simply return the same base URI instance for this same-document reference
+ return $base;
+ }
+
+ if ($rel->getScheme() != '') {
+ return $rel->withPath(self::removeDotSegments($rel->getPath()));
+ }
+
+ if ($rel->getAuthority() != '') {
+ $targetAuthority = $rel->getAuthority();
+ $targetPath = self::removeDotSegments($rel->getPath());
+ $targetQuery = $rel->getQuery();
+ } else {
+ $targetAuthority = $base->getAuthority();
+ if ($rel->getPath() === '') {
+ $targetPath = $base->getPath();
+ $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery();
+ } else {
+ if ($rel->getPath()[0] === '/') {
+ $targetPath = $rel->getPath();
+ } else {
+ if ($targetAuthority != '' && $base->getPath() === '') {
+ $targetPath = '/' . $rel->getPath();
+ } else {
+ $lastSlashPos = strrpos($base->getPath(), '/');
+ if ($lastSlashPos === false) {
+ $targetPath = $rel->getPath();
+ } else {
+ $targetPath = substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath();
+ }
+ }
+ }
+ $targetPath = self::removeDotSegments($targetPath);
+ $targetQuery = $rel->getQuery();
+ }
+ }
+
+ return new Uri(Uri::composeComponents(
+ $base->getScheme(),
+ $targetAuthority,
+ $targetPath,
+ $targetQuery,
+ $rel->getFragment()
+ ));
+ }
+
+ /**
+ * Returns the target URI as a relative reference from the base URI.
+ *
+ * This method is the counterpart to resolve():
+ *
+ * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
+ *
+ * One use-case is to use the current request URI as base URI and then generate relative links in your documents
+ * to reduce the document size or offer self-contained downloadable document archives.
+ *
+ * $base = new Uri('http://example.com/a/b/');
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
+ * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
+ * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
+ *
+ * This method also accepts a target that is already relative and will try to relativize it further. Only a
+ * relative-path reference will be returned as-is.
+ *
+ * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well
+ *
+ * @param UriInterface $base Base URI
+ * @param UriInterface $target Target URI
+ *
+ * @return UriInterface The relative URI reference
+ */
+ public static function relativize(UriInterface $base, UriInterface $target)
+ {
+ if ($target->getScheme() !== '' &&
+ ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')
+ ) {
+ return $target;
+ }
+
+ if (Uri::isRelativePathReference($target)) {
+ // As the target is already highly relative we return it as-is. It would be possible to resolve
+ // the target with `$target = self::resolve($base, $target);` and then try make it more relative
+ // by removing a duplicate query. But let's not do that automatically.
+ return $target;
+ }
+
+ if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) {
+ return $target->withScheme('');
+ }
+
+ // We must remove the path before removing the authority because if the path starts with two slashes, the URI
+ // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
+ // invalid.
+ $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost('');
+
+ if ($base->getPath() !== $target->getPath()) {
+ return $emptyPathUri->withPath(self::getRelativePath($base, $target));
+ }
+
+ if ($base->getQuery() === $target->getQuery()) {
+ // Only the target fragment is left. And it must be returned even if base and target fragment are the same.
+ return $emptyPathUri->withQuery('');
+ }
+
+ // If the base URI has a query but the target has none, we cannot return an empty path reference as it would
+ // inherit the base query component when resolving.
+ if ($target->getQuery() === '') {
+ $segments = explode('/', $target->getPath());
+ $lastSegment = end($segments);
+
+ return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment);
+ }
+
+ return $emptyPathUri;
+ }
+
+ private static function getRelativePath(UriInterface $base, UriInterface $target)
+ {
+ $sourceSegments = explode('/', $base->getPath());
+ $targetSegments = explode('/', $target->getPath());
+ array_pop($sourceSegments);
+ $targetLastSegment = array_pop($targetSegments);
+ foreach ($sourceSegments as $i => $segment) {
+ if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
+ unset($sourceSegments[$i], $targetSegments[$i]);
+ } else {
+ break;
+ }
+ }
+ $targetSegments[] = $targetLastSegment;
+ $relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);
+
+ // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
+ if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
+ $relativePath = "./$relativePath";
+ } elseif ('/' === $relativePath[0]) {
+ if ($base->getAuthority() != '' && $base->getPath() === '') {
+ // In this case an extra slash is added by resolve() automatically. So we must not add one here.
+ $relativePath = ".$relativePath";
+ } else {
+ $relativePath = "./$relativePath";
+ }
+ }
+
+ return $relativePath;
+ }
+
+ private function __construct()
+ {
+ // cannot be instantiated
+ }
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/functions.php b/bin/wiki/vendor/guzzlehttp/psr7/src/functions.php
new file mode 100644
index 00000000..957bfb42
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/functions.php
@@ -0,0 +1,898 @@
+<?php
+namespace GuzzleHttp\Psr7;
+
+use Psr\Http\Message\MessageInterface;
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Returns the string representation of an HTTP message.
+ *
+ * @param MessageInterface $message Message to convert to a string.
+ *
+ * @return string
+ */
+function str(MessageInterface $message)
+{
+ if ($message instanceof RequestInterface) {
+ $msg = trim($message->getMethod() . ' '
+ . $message->getRequestTarget())
+ . ' HTTP/' . $message->getProtocolVersion();
+ if (!$message->hasHeader('host')) {
+ $msg .= "\r\nHost: " . $message->getUri()->getHost();
+ }
+ } elseif ($message instanceof ResponseInterface) {
+ $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
+ . $message->getStatusCode() . ' '
+ . $message->getReasonPhrase();
+ } else {
+ throw new \InvalidArgumentException('Unknown message type');
+ }
+
+ foreach ($message->getHeaders() as $name => $values) {
+ $msg .= "\r\n{$name}: " . implode(', ', $values);
+ }
+
+ return "{$msg}\r\n\r\n" . $message->getBody();
+}
+
+/**
+ * Returns a UriInterface for the given value.
+ *
+ * This function accepts a string or {@see Psr\Http\Message\UriInterface} and
+ * returns a UriInterface for the given value. If the value is already a
+ * `UriInterface`, it is returned as-is.
+ *
+ * @param string|UriInterface $uri
+ *
+ * @return UriInterface
+ * @throws \InvalidArgumentException
+ */
+function uri_for($uri)
+{
+ if ($uri instanceof UriInterface) {
+ return $uri;
+ } elseif (is_string($uri)) {
+ return new Uri($uri);
+ }
+
+ throw new \InvalidArgumentException('URI must be a string or UriInterface');
+}
+
+/**
+ * Create a new stream based on the input type.
+ *
+ * Options is an associative array that can contain the following keys:
+ * - metadata: Array of custom metadata.
+ * - size: Size of the stream.
+ *
+ * @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data
+ * @param array $options Additional options
+ *
+ * @return StreamInterface
+ * @throws \InvalidArgumentException if the $resource arg is not valid.
+ */
+function stream_for($resource = '', array $options = [])
+{
+ if (is_scalar($resource)) {
+ $stream = fopen('php://temp', 'r+');
+ if ($resource !== '') {
+ fwrite($stream, $resource);
+ fseek($stream, 0);
+ }
+ return new Stream($stream, $options);
+ }
+
+ switch (gettype($resource)) {
+ case 'resource':
+ return new Stream($resource, $options);
+ case 'object':
+ if ($resource instanceof StreamInterface) {
+ return $resource;
+ } elseif ($resource instanceof \Iterator) {
+ return new PumpStream(function () use ($resource) {
+ if (!$resource->valid()) {
+ return false;
+ }
+ $result = $resource->current();
+ $resource->next();
+ return $result;
+ }, $options);
+ } elseif (method_exists($resource, '__toString')) {
+ return stream_for((string) $resource, $options);
+ }
+ break;
+ case 'NULL':
+ return new Stream(fopen('php://temp', 'r+'), $options);
+ }
+
+ if (is_callable($resource)) {
+ return new PumpStream($resource, $options);
+ }
+
+ throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
+}
+
+/**
+ * Parse an array of header values containing ";" separated data into an
+ * array of associative arrays representing the header key value pair
+ * data of the header. When a parameter does not contain a value, but just
+ * contains a key, this function will inject a key with a '' string value.
+ *
+ * @param string|array $header Header to parse into components.
+ *
+ * @return array Returns the parsed header values.
+ */
+function parse_header($header)
+{
+ static $trimmed = "\"' \n\t\r";
+ $params = $matches = [];
+
+ foreach (normalize_header($header) as $val) {
+ $part = [];
+ foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
+ if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
+ $m = $matches[0];
+ if (isset($m[1])) {
+ $part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
+ } else {
+ $part[] = trim($m[0], $trimmed);
+ }
+ }
+ }
+ if ($part) {
+ $params[] = $part;
+ }
+ }
+
+ return $params;
+}
+
+/**
+ * Converts an array of header values that may contain comma separated
+ * headers into an array of headers with no comma separated values.
+ *
+ * @param string|array $header Header to normalize.
+ *
+ * @return array Returns the normalized header field values.
+ */
+function normalize_header($header)
+{
+ if (!is_array($header)) {
+ return array_map('trim', explode(',', $header));
+ }
+
+ $result = [];
+ foreach ($header as $value) {
+ foreach ((array) $value as $v) {
+ if (strpos($v, ',') === false) {
+ $result[] = $v;
+ continue;
+ }
+ foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
+ $result[] = trim($vv);
+ }
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Clone and modify a request with the given changes.
+ *
+ * The changes can be one of:
+ * - method: (string) Changes the HTTP method.
+ * - set_headers: (array) Sets the given headers.
+ * - remove_headers: (array) Remove the given headers.
+ * - body: (mixed) Sets the given body.
+ * - uri: (UriInterface) Set the URI.
+ * - query: (string) Set the query string value of the URI.
+ * - version: (string) Set the protocol version.
+ *
+ * @param RequestInterface $request Request to clone and modify.
+ * @param array $changes Changes to apply.
+ *
+ * @return RequestInterface
+ */
+function modify_request(RequestInterface $request, array $changes)
+{
+ if (!$changes) {
+ return $request;
+ }
+
+ $headers = $request->getHeaders();
+
+ if (!isset($changes['uri'])) {
+ $uri = $request->getUri();
+ } else {
+ // Remove the host header if one is on the URI
+ if ($host = $changes['uri']->getHost()) {
+ $changes['set_headers']['Host'] = $host;
+
+ if ($port = $changes['uri']->getPort()) {
+ $standardPorts = ['http' => 80, 'https' => 443];
+ $scheme = $changes['uri']->getScheme();
+ if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
+ $changes['set_headers']['Host'] .= ':'.$port;
+ }
+ }
+ }
+ $uri = $changes['uri'];
+ }
+
+ if (!empty($changes['remove_headers'])) {
+ $headers = _caseless_remove($changes['remove_headers'], $headers);
+ }
+
+ if (!empty($changes['set_headers'])) {
+ $headers = _caseless_remove(array_keys($changes['set_headers']), $headers);
+ $headers = $changes['set_headers'] + $headers;
+ }
+
+ if (isset($changes['query'])) {
+ $uri = $uri->withQuery($changes['query']);
+ }
+
+ if ($request instanceof ServerRequestInterface) {
+ return (new ServerRequest(
+ isset($changes['method']) ? $changes['method'] : $request->getMethod(),
+ $uri,
+ $headers,
+ isset($changes['body']) ? $changes['body'] : $request->getBody(),
+ isset($changes['version'])
+ ? $changes['version']
+ : $request->getProtocolVersion(),
+ $request->getServerParams()
+ ))
+ ->withParsedBody($request->getParsedBody())
+ ->withQueryParams($request->getQueryParams())
+ ->withCookieParams($request->getCookieParams())
+ ->withUploadedFiles($request->getUploadedFiles());
+ }
+
+ return new Request(
+ isset($changes['method']) ? $changes['method'] : $request->getMethod(),
+ $uri,
+ $headers,
+ isset($changes['body']) ? $changes['body'] : $request->getBody(),
+ isset($changes['version'])
+ ? $changes['version']
+ : $request->getProtocolVersion()
+ );
+}
+
+/**
+ * Attempts to rewind a message body and throws an exception on failure.
+ *
+ * The body of the message will only be rewound if a call to `tell()` returns a
+ * value other than `0`.
+ *
+ * @param MessageInterface $message Message to rewind
+ *
+ * @throws \RuntimeException
+ */
+function rewind_body(MessageInterface $message)
+{
+ $body = $message->getBody();
+
+ if ($body->tell()) {
+ $body->rewind();
+ }
+}
+
+/**
+ * Safely opens a PHP stream resource using a filename.
+ *
+ * When fopen fails, PHP normally raises a warning. This function adds an
+ * error handler that checks for errors and throws an exception instead.
+ *
+ * @param string $filename File to open
+ * @param string $mode Mode used to open the file
+ *
+ * @return resource
+ * @throws \RuntimeException if the file cannot be opened
+ */
+function try_fopen($filename, $mode)
+{
+ $ex = null;
+ set_error_handler(function () use ($filename, $mode, &$ex) {
+ $ex = new \RuntimeException(sprintf(
+ 'Unable to open %s using mode %s: %s',
+ $filename,
+ $mode,
+ func_get_args()[1]
+ ));
+ });
+
+ $handle = fopen($filename, $mode);
+ restore_error_handler();
+
+ if ($ex) {
+ /** @var $ex \RuntimeException */
+ throw $ex;
+ }
+
+ return $handle;
+}
+
+/**
+ * Copy the contents of a stream into a string until the given number of
+ * bytes have been read.
+ *
+ * @param StreamInterface $stream Stream to read
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
+ * to read the entire stream.
+ * @return string
+ * @throws \RuntimeException on error.
+ */
+function copy_to_string(StreamInterface $stream, $maxLen = -1)
+{
+ $buffer = '';
+
+ if ($maxLen === -1) {
+ while (!$stream->eof()) {
+ $buf = $stream->read(1048576);
+ // Using a loose equality here to match on '' and false.
+ if ($buf == null) {
+ break;
+ }
+ $buffer .= $buf;
+ }
+ return $buffer;
+ }
+
+ $len = 0;
+ while (!$stream->eof() && $len < $maxLen) {
+ $buf = $stream->read($maxLen - $len);
+ // Using a loose equality here to match on '' and false.
+ if ($buf == null) {
+ break;
+ }
+ $buffer .= $buf;
+ $len = strlen($buffer);
+ }
+
+ return $buffer;
+}
+
+/**
+ * Copy the contents of a stream into another stream until the given number
+ * of bytes have been read.
+ *
+ * @param StreamInterface $source Stream to read from
+ * @param StreamInterface $dest Stream to write to
+ * @param int $maxLen Maximum number of bytes to read. Pass -1
+ * to read the entire stream.
+ *
+ * @throws \RuntimeException on error.
+ */
+function copy_to_stream(
+ StreamInterface $source,
+ StreamInterface $dest,
+ $maxLen = -1
+) {
+ $bufferSize = 8192;
+
+ if ($maxLen === -1) {
+ while (!$source->eof()) {
+ if (!$dest->write($source->read($bufferSize))) {
+ break;
+ }
+ }
+ } else {
+ $remaining = $maxLen;
+ while ($remaining > 0 && !$source->eof()) {
+ $buf = $source->read(min($bufferSize, $remaining));
+ $len = strlen($buf);
+ if (!$len) {
+ break;
+ }
+ $remaining -= $len;
+ $dest->write($buf);
+ }
+ }
+}
+
+/**
+ * Calculate a hash of a Stream
+ *
+ * @param StreamInterface $stream Stream to calculate the hash for
+ * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
+ * @param bool $rawOutput Whether or not to use raw output
+ *
+ * @return string Returns the hash of the stream
+ * @throws \RuntimeException on error.
+ */
+function hash(
+ StreamInterface $stream,
+ $algo,
+ $rawOutput = false
+) {
+ $pos = $stream->tell();
+
+ if ($pos > 0) {
+ $stream->rewind();
+ }
+
+ $ctx = hash_init($algo);
+ while (!$stream->eof()) {
+ hash_update($ctx, $stream->read(1048576));
+ }
+
+ $out = hash_final($ctx, (bool) $rawOutput);
+ $stream->seek($pos);
+
+ return $out;
+}
+
+/**
+ * Read a line from the stream up to the maximum allowed buffer length
+ *
+ * @param StreamInterface $stream Stream to read from
+ * @param int $maxLength Maximum buffer length
+ *
+ * @return string
+ */
+function readline(StreamInterface $stream, $maxLength = null)
+{
+ $buffer = '';
+ $size = 0;
+
+ while (!$stream->eof()) {
+ // Using a loose equality here to match on '' and false.
+ if (null == ($byte = $stream->read(1))) {
+ return $buffer;
+ }
+ $buffer .= $byte;
+ // Break when a new line is found or the max length - 1 is reached
+ if ($byte === "\n" || ++$size === $maxLength - 1) {
+ break;
+ }
+ }
+
+ return $buffer;
+}
+
+/**
+ * Parses a request message string into a request object.
+ *
+ * @param string $message Request message string.
+ *
+ * @return Request
+ */
+function parse_request($message)
+{
+ $data = _parse_message($message);
+ $matches = [];
+ if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
+ throw new \InvalidArgumentException('Invalid request string');
+ }
+ $parts = explode(' ', $data['start-line'], 3);
+ $version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
+
+ $request = new Request(
+ $parts[0],
+ $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
+ $data['headers'],
+ $data['body'],
+ $version
+ );
+
+ return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
+}
+
+/**
+ * Parses a response message string into a response object.
+ *
+ * @param string $message Response message string.
+ *
+ * @return Response
+ */
+function parse_response($message)
+{
+ $data = _parse_message($message);
+ // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
+ // between status-code and reason-phrase is required. But browsers accept
+ // responses without space and reason as well.
+ if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
+ throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
+ }
+ $parts = explode(' ', $data['start-line'], 3);
+
+ return new Response(
+ $parts[1],
+ $data['headers'],
+ $data['body'],
+ explode('/', $parts[0])[1],
+ isset($parts[2]) ? $parts[2] : null
+ );
+}
+
+/**
+ * Parse a query string into an associative array.
+ *
+ * If multiple values are found for the same key, the value of that key
+ * value pair will become an array. This function does not parse nested
+ * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
+ * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
+ *
+ * @param string $str Query string to parse
+ * @param int|bool $urlEncoding How the query string is encoded
+ *
+ * @return array
+ */
+function parse_query($str, $urlEncoding = true)
+{
+ $result = [];
+
+ if ($str === '') {
+ return $result;
+ }
+
+ if ($urlEncoding === true) {
+ $decoder = function ($value) {
+ return rawurldecode(str_replace('+', ' ', $value));
+ };
+ } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
+ $decoder = 'rawurldecode';
+ } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
+ $decoder = 'urldecode';
+ } else {
+ $decoder = function ($str) { return $str; };
+ }
+
+ foreach (explode('&', $str) as $kvp) {
+ $parts = explode('=', $kvp, 2);
+ $key = $decoder($parts[0]);
+ $value = isset($parts[1]) ? $decoder($parts[1]) : null;
+ if (!isset($result[$key])) {
+ $result[$key] = $value;
+ } else {
+ if (!is_array($result[$key])) {
+ $result[$key] = [$result[$key]];
+ }
+ $result[$key][] = $value;
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Build a query string from an array of key value pairs.
+ *
+ * This function can use the return value of parse_query() to build a query
+ * string. This function does not modify the provided keys when an array is
+ * encountered (like http_build_query would).
+ *
+ * @param array $params Query string parameters.
+ * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
+ * to encode using RFC3986, or PHP_QUERY_RFC1738
+ * to encode using RFC1738.
+ * @return string
+ */
+function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
+{
+ if (!$params) {
+ return '';
+ }
+
+ if ($encoding === false) {
+ $encoder = function ($str) { return $str; };
+ } elseif ($encoding === PHP_QUERY_RFC3986) {
+ $encoder = 'rawurlencode';
+ } elseif ($encoding === PHP_QUERY_RFC1738) {
+ $encoder = 'urlencode';
+ } else {
+ throw new \InvalidArgumentException('Invalid type');
+ }
+
+ $qs = '';
+ foreach ($params as $k => $v) {
+ $k = $encoder($k);
+ if (!is_array($v)) {
+ $qs .= $k;
+ if ($v !== null) {
+ $qs .= '=' . $encoder($v);
+ }
+ $qs .= '&';
+ } else {
+ foreach ($v as $vv) {
+ $qs .= $k;
+ if ($vv !== null) {
+ $qs .= '=' . $encoder($vv);
+ }
+ $qs .= '&';
+ }
+ }
+ }
+
+ return $qs ? (string) substr($qs, 0, -1) : '';
+}
+
+/**
+ * Determines the mimetype of a file by looking at its extension.
+ *
+ * @param $filename
+ *
+ * @return null|string
+ */
+function mimetype_from_filename($filename)
+{
+ return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));
+}
+
+/**
+ * Maps a file extensions to a mimetype.
+ *
+ * @param $extension string The file extension.
+ *
+ * @return string|null
+ * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
+ */
+function mimetype_from_extension($extension)
+{
+ static $mimetypes = [
+ '3gp' => 'video/3gpp',
+ '7z' => 'application/x-7z-compressed',
+ 'aac' => 'audio/x-aac',
+ 'ai' => 'application/postscript',
+ 'aif' => 'audio/x-aiff',
+ 'asc' => 'text/plain',
+ 'asf' => 'video/x-ms-asf',
+ 'atom' => 'application/atom+xml',
+ 'avi' => 'video/x-msvideo',
+ 'bmp' => 'image/bmp',
+ 'bz2' => 'application/x-bzip2',
+ 'cer' => 'application/pkix-cert',
+ 'crl' => 'application/pkix-crl',
+ 'crt' => 'application/x-x509-ca-cert',
+ 'css' => 'text/css',
+ 'csv' => 'text/csv',
+ 'cu' => 'application/cu-seeme',
+ 'deb' => 'application/x-debian-package',
+ 'doc' => 'application/msword',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dvi' => 'application/x-dvi',
+ 'eot' => 'application/vnd.ms-fontobject',
+ 'eps' => 'application/postscript',
+ 'epub' => 'application/epub+zip',
+ 'etx' => 'text/x-setext',
+ 'flac' => 'audio/flac',
+ 'flv' => 'video/x-flv',
+ 'gif' => 'image/gif',
+ 'gz' => 'application/gzip',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'ico' => 'image/x-icon',
+ 'ics' => 'text/calendar',
+ 'ini' => 'text/plain',
+ 'iso' => 'application/x-iso9660-image',
+ 'jar' => 'application/java-archive',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'js' => 'text/javascript',
+ 'json' => 'application/json',
+ 'latex' => 'application/x-latex',
+ 'log' => 'text/plain',
+ 'm4a' => 'audio/mp4',
+ 'm4v' => 'video/mp4',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mov' => 'video/quicktime',
+ 'mkv' => 'video/x-matroska',
+ 'mp3' => 'audio/mpeg',
+ 'mp4' => 'video/mp4',
+ 'mp4a' => 'audio/mp4',
+ 'mp4v' => 'video/mp4',
+ 'mpe' => 'video/mpeg',
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpg4' => 'video/mp4',
+ 'oga' => 'audio/ogg',
+ 'ogg' => 'audio/ogg',
+ 'ogv' => 'video/ogg',
+ 'ogx' => 'application/ogg',
+ 'pbm' => 'image/x-portable-bitmap',
+ 'pdf' => 'application/pdf',
+ 'pgm' => 'image/x-portable-graymap',
+ 'png' => 'image/png',
+ 'pnm' => 'image/x-portable-anymap',
+ 'ppm' => 'image/x-portable-pixmap',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'ps' => 'application/postscript',
+ 'qt' => 'video/quicktime',
+ 'rar' => 'application/x-rar-compressed',
+ 'ras' => 'image/x-cmu-raster',
+ 'rss' => 'application/rss+xml',
+ 'rtf' => 'application/rtf',
+ 'sgm' => 'text/sgml',
+ 'sgml' => 'text/sgml',
+ 'svg' => 'image/svg+xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'tar' => 'application/x-tar',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'torrent' => 'application/x-bittorrent',
+ 'ttf' => 'application/x-font-ttf',
+ 'txt' => 'text/plain',
+ 'wav' => 'audio/x-wav',
+ 'webm' => 'video/webm',
+ 'wma' => 'audio/x-ms-wma',
+ 'wmv' => 'video/x-ms-wmv',
+ 'woff' => 'application/x-font-woff',
+ 'wsdl' => 'application/wsdl+xml',
+ 'xbm' => 'image/x-xbitmap',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xml' => 'application/xml',
+ 'xpm' => 'image/x-xpixmap',
+ 'xwd' => 'image/x-xwindowdump',
+ 'yaml' => 'text/yaml',
+ 'yml' => 'text/yaml',
+ 'zip' => 'application/zip',
+ ];
+
+ $extension = strtolower($extension);
+
+ return isset($mimetypes[$extension])
+ ? $mimetypes[$extension]
+ : null;
+}
+
+/**
+ * Parses an HTTP message into an associative array.
+ *
+ * The array contains the "start-line" key containing the start line of
+ * the message, "headers" key containing an associative array of header
+ * array values, and a "body" key containing the body of the message.
+ *
+ * @param string $message HTTP request or response to parse.
+ *
+ * @return array
+ * @internal
+ */
+function _parse_message($message)
+{
+ if (!$message) {
+ throw new \InvalidArgumentException('Invalid message');
+ }
+
+ $message = ltrim($message, "\r\n");
+
+ $messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
+
+ if ($messageParts === false || count($messageParts) !== 2) {
+ throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
+ }
+
+ list($rawHeaders, $body) = $messageParts;
+ $rawHeaders .= "\r\n"; // Put back the delimiter we split previously
+ $headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
+
+ if ($headerParts === false || count($headerParts) !== 2) {
+ throw new \InvalidArgumentException('Invalid message: Missing status line');
+ }
+
+ list($startLine, $rawHeaders) = $headerParts;
+
+ if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
+ // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
+ $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
+ }
+
+ /** @var array[] $headerLines */
+ $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
+
+ // If these aren't the same, then one line didn't match and there's an invalid header.
+ if ($count !== substr_count($rawHeaders, "\n")) {
+ // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
+ if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
+ throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
+ }
+
+ throw new \InvalidArgumentException('Invalid header syntax');
+ }
+
+ $headers = [];
+
+ foreach ($headerLines as $headerLine) {
+ $headers[$headerLine[1]][] = $headerLine[2];
+ }
+
+ return [
+ 'start-line' => $startLine,
+ 'headers' => $headers,
+ 'body' => $body,
+ ];
+}
+
+/**
+ * Constructs a URI for an HTTP request message.
+ *
+ * @param string $path Path from the start-line
+ * @param array $headers Array of headers (each value an array).
+ *
+ * @return string
+ * @internal
+ */
+function _parse_request_uri($path, array $headers)
+{
+ $hostKey = array_filter(array_keys($headers), function ($k) {
+ return strtolower($k) === 'host';
+ });
+
+ // If no host is found, then a full URI cannot be constructed.
+ if (!$hostKey) {
+ return $path;
+ }
+
+ $host = $headers[reset($hostKey)][0];
+ $scheme = substr($host, -4) === ':443' ? 'https' : 'http';
+
+ return $scheme . '://' . $host . '/' . ltrim($path, '/');
+}
+
+/**
+ * Get a short summary of the message body
+ *
+ * Will return `null` if the response is not printable.
+ *
+ * @param MessageInterface $message The message to get the body summary
+ * @param int $truncateAt The maximum allowed size of the summary
+ *
+ * @return null|string
+ */
+function get_message_body_summary(MessageInterface $message, $truncateAt = 120)
+{
+ $body = $message->getBody();
+
+ if (!$body->isSeekable() || !$body->isReadable()) {
+ return null;
+ }
+
+ $size = $body->getSize();
+
+ if ($size === 0) {
+ return null;
+ }
+
+ $summary = $body->read($truncateAt);
+ $body->rewind();
+
+ if ($size > $truncateAt) {
+ $summary .= ' (truncated...)';
+ }
+
+ // Matches any printable character, including unicode characters:
+ // letters, marks, numbers, punctuation, spacing, and separators.
+ if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
+ return null;
+ }
+
+ return $summary;
+}
+
+/** @internal */
+function _caseless_remove($keys, array $data)
+{
+ $result = [];
+
+ foreach ($keys as &$key) {
+ $key = strtolower($key);
+ }
+
+ foreach ($data as $k => $v) {
+ if (!in_array(strtolower($k), $keys)) {
+ $result[$k] = $v;
+ }
+ }
+
+ return $result;
+}
diff --git a/bin/wiki/vendor/guzzlehttp/psr7/src/functions_include.php b/bin/wiki/vendor/guzzlehttp/psr7/src/functions_include.php
new file mode 100644
index 00000000..96a4a83a
--- /dev/null
+++ b/bin/wiki/vendor/guzzlehttp/psr7/src/functions_include.php
@@ -0,0 +1,6 @@
+<?php
+
+// Don't redefine the functions if included multiple times.
+if (!function_exists('GuzzleHttp\Psr7\str')) {
+ require __DIR__ . '/functions.php';
+}
diff --git a/bin/wiki/vendor/psr/http-message/CHANGELOG.md b/bin/wiki/vendor/psr/http-message/CHANGELOG.md
new file mode 100644
index 00000000..74b1ef92
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/CHANGELOG.md
@@ -0,0 +1,36 @@
+# Changelog
+
+All notable changes to this project will be documented in this file, in reverse chronological order by release.
+
+## 1.0.1 - 2016-08-06
+
+### Added
+
+- Nothing.
+
+### Deprecated
+
+- Nothing.
+
+### Removed
+
+- Nothing.
+
+### Fixed
+
+- Updated all `@return self` annotation references in interfaces to use
+ `@return static`, which more closelly follows the semantics of the
+ specification.
+- Updated the `MessageInterface::getHeaders()` return annotation to use the
+ value `string[][]`, indicating the format is a nested array of strings.
+- Updated the `@link` annotation for `RequestInterface::withRequestTarget()`
+ to point to the correct section of RFC 7230.
+- Updated the `ServerRequestInterface::withUploadedFiles()` parameter annotation
+ to add the parameter name (`$uploadedFiles`).
+- Updated a `@throws` annotation for the `UploadedFileInterface::moveTo()`
+ method to correctly reference the method parameter (it was referencing an
+ incorrect parameter name previously).
+
+## 1.0.0 - 2016-05-18
+
+Initial stable release; reflects accepted PSR-7 specification.
diff --git a/bin/wiki/vendor/psr/http-message/LICENSE b/bin/wiki/vendor/psr/http-message/LICENSE
new file mode 100644
index 00000000..c2d8e452
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2014 PHP Framework Interoperability Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/bin/wiki/vendor/psr/http-message/README.md b/bin/wiki/vendor/psr/http-message/README.md
new file mode 100644
index 00000000..28185338
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/README.md
@@ -0,0 +1,13 @@
+PSR Http Message
+================
+
+This repository holds all interfaces/classes/traits related to
+[PSR-7](http://www.php-fig.org/psr/psr-7/).
+
+Note that this is not a HTTP message implementation of its own. It is merely an
+interface that describes a HTTP message. See the specification for more details.
+
+Usage
+-----
+
+We'll certainly need some stuff in here. \ No newline at end of file
diff --git a/bin/wiki/vendor/psr/http-message/composer.json b/bin/wiki/vendor/psr/http-message/composer.json
new file mode 100644
index 00000000..b0d2937a
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "psr/http-message",
+ "description": "Common interface for HTTP messages",
+ "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"],
+ "homepage": "https://github.com/php-fig/http-message",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ }
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/MessageInterface.php b/bin/wiki/vendor/psr/http-message/src/MessageInterface.php
new file mode 100644
index 00000000..dd46e5ec
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/MessageInterface.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * HTTP messages consist of requests from a client to a server and responses
+ * from a server to a client. This interface defines the methods common to
+ * each.
+ *
+ * Messages are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ *
+ * @link http://www.ietf.org/rfc/rfc7230.txt
+ * @link http://www.ietf.org/rfc/rfc7231.txt
+ */
+interface MessageInterface
+{
+ /**
+ * Retrieves the HTTP protocol version as a string.
+ *
+ * The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
+ *
+ * @return string HTTP protocol version.
+ */
+ public function getProtocolVersion();
+
+ /**
+ * Return an instance with the specified HTTP protocol version.
+ *
+ * The version string MUST contain only the HTTP version number (e.g.,
+ * "1.1", "1.0").
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new protocol version.
+ *
+ * @param string $version HTTP protocol version
+ * @return static
+ */
+ public function withProtocolVersion($version);
+
+ /**
+ * Retrieves all message header values.
+ *
+ * The keys represent the header name as it will be sent over the wire, and
+ * each value is an array of strings associated with the header.
+ *
+ * // Represent the headers as a string
+ * foreach ($message->getHeaders() as $name => $values) {
+ * echo $name . ": " . implode(", ", $values);
+ * }
+ *
+ * // Emit headers iteratively:
+ * foreach ($message->getHeaders() as $name => $values) {
+ * foreach ($values as $value) {
+ * header(sprintf('%s: %s', $name, $value), false);
+ * }
+ * }
+ *
+ * While header names are not case-sensitive, getHeaders() will preserve the
+ * exact case in which headers were originally specified.
+ *
+ * @return string[][] Returns an associative array of the message's headers. Each
+ * key MUST be a header name, and each value MUST be an array of strings
+ * for that header.
+ */
+ public function getHeaders();
+
+ /**
+ * Checks if a header exists by the given case-insensitive name.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @return bool Returns true if any header names match the given header
+ * name using a case-insensitive string comparison. Returns false if
+ * no matching header name is found in the message.
+ */
+ public function hasHeader($name);
+
+ /**
+ * Retrieves a message header value by the given case-insensitive name.
+ *
+ * This method returns an array of all the header values of the given
+ * case-insensitive header name.
+ *
+ * If the header does not appear in the message, this method MUST return an
+ * empty array.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @return string[] An array of string values as provided for the given
+ * header. If the header does not appear in the message, this method MUST
+ * return an empty array.
+ */
+ public function getHeader($name);
+
+ /**
+ * Retrieves a comma-separated string of the values for a single header.
+ *
+ * This method returns all of the header values of the given
+ * case-insensitive header name as a string concatenated together using
+ * a comma.
+ *
+ * NOTE: Not all header values may be appropriately represented using
+ * comma concatenation. For such headers, use getHeader() instead
+ * and supply your own delimiter when concatenating.
+ *
+ * If the header does not appear in the message, this method MUST return
+ * an empty string.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @return string A string of values as provided for the given header
+ * concatenated together using a comma. If the header does not appear in
+ * the message, this method MUST return an empty string.
+ */
+ public function getHeaderLine($name);
+
+ /**
+ * Return an instance with the provided value replacing the specified header.
+ *
+ * While header names are case-insensitive, the casing of the header will
+ * be preserved by this function, and returned from getHeaders().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new and/or updated header and value.
+ *
+ * @param string $name Case-insensitive header field name.
+ * @param string|string[] $value Header value(s).
+ * @return static
+ * @throws \InvalidArgumentException for invalid header names or values.
+ */
+ public function withHeader($name, $value);
+
+ /**
+ * Return an instance with the specified header appended with the given value.
+ *
+ * Existing values for the specified header will be maintained. The new
+ * value(s) will be appended to the existing list. If the header did not
+ * exist previously, it will be added.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new header and/or value.
+ *
+ * @param string $name Case-insensitive header field name to add.
+ * @param string|string[] $value Header value(s).
+ * @return static
+ * @throws \InvalidArgumentException for invalid header names or values.
+ */
+ public function withAddedHeader($name, $value);
+
+ /**
+ * Return an instance without the specified header.
+ *
+ * Header resolution MUST be done without case-sensitivity.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that removes
+ * the named header.
+ *
+ * @param string $name Case-insensitive header field name to remove.
+ * @return static
+ */
+ public function withoutHeader($name);
+
+ /**
+ * Gets the body of the message.
+ *
+ * @return StreamInterface Returns the body as a stream.
+ */
+ public function getBody();
+
+ /**
+ * Return an instance with the specified message body.
+ *
+ * The body MUST be a StreamInterface object.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return a new instance that has the
+ * new body stream.
+ *
+ * @param StreamInterface $body Body.
+ * @return static
+ * @throws \InvalidArgumentException When the body is not valid.
+ */
+ public function withBody(StreamInterface $body);
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/RequestInterface.php b/bin/wiki/vendor/psr/http-message/src/RequestInterface.php
new file mode 100644
index 00000000..a96d4fd6
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/RequestInterface.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Representation of an outgoing, client-side request.
+ *
+ * Per the HTTP specification, this interface includes properties for
+ * each of the following:
+ *
+ * - Protocol version
+ * - HTTP method
+ * - URI
+ * - Headers
+ * - Message body
+ *
+ * During construction, implementations MUST attempt to set the Host header from
+ * a provided URI if no Host header is provided.
+ *
+ * Requests are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ */
+interface RequestInterface extends MessageInterface
+{
+ /**
+ * Retrieves the message's request target.
+ *
+ * Retrieves the message's request-target either as it will appear (for
+ * clients), as it appeared at request (for servers), or as it was
+ * specified for the instance (see withRequestTarget()).
+ *
+ * In most cases, this will be the origin-form of the composed URI,
+ * unless a value was provided to the concrete implementation (see
+ * withRequestTarget() below).
+ *
+ * If no URI is available, and no request-target has been specifically
+ * provided, this method MUST return the string "/".
+ *
+ * @return string
+ */
+ public function getRequestTarget();
+
+ /**
+ * Return an instance with the specific request-target.
+ *
+ * If the request needs a non-origin-form request-target — e.g., for
+ * specifying an absolute-form, authority-form, or asterisk-form —
+ * this method may be used to create an instance with the specified
+ * request-target, verbatim.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * changed request target.
+ *
+ * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
+ * request-target forms allowed in request messages)
+ * @param mixed $requestTarget
+ * @return static
+ */
+ public function withRequestTarget($requestTarget);
+
+ /**
+ * Retrieves the HTTP method of the request.
+ *
+ * @return string Returns the request method.
+ */
+ public function getMethod();
+
+ /**
+ * Return an instance with the provided HTTP method.
+ *
+ * While HTTP method names are typically all uppercase characters, HTTP
+ * method names are case-sensitive and thus implementations SHOULD NOT
+ * modify the given string.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * changed request method.
+ *
+ * @param string $method Case-sensitive method.
+ * @return static
+ * @throws \InvalidArgumentException for invalid HTTP methods.
+ */
+ public function withMethod($method);
+
+ /**
+ * Retrieves the URI instance.
+ *
+ * This method MUST return a UriInterface instance.
+ *
+ * @link http://tools.ietf.org/html/rfc3986#section-4.3
+ * @return UriInterface Returns a UriInterface instance
+ * representing the URI of the request.
+ */
+ public function getUri();
+
+ /**
+ * Returns an instance with the provided URI.
+ *
+ * This method MUST update the Host header of the returned request by
+ * default if the URI contains a host component. If the URI does not
+ * contain a host component, any pre-existing Host header MUST be carried
+ * over to the returned request.
+ *
+ * You can opt-in to preserving the original state of the Host header by
+ * setting `$preserveHost` to `true`. When `$preserveHost` is set to
+ * `true`, this method interacts with the Host header in the following ways:
+ *
+ * - If the Host header is missing or empty, and the new URI contains
+ * a host component, this method MUST update the Host header in the returned
+ * request.
+ * - If the Host header is missing or empty, and the new URI does not contain a
+ * host component, this method MUST NOT update the Host header in the returned
+ * request.
+ * - If a Host header is present and non-empty, this method MUST NOT update
+ * the Host header in the returned request.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * new UriInterface instance.
+ *
+ * @link http://tools.ietf.org/html/rfc3986#section-4.3
+ * @param UriInterface $uri New request URI to use.
+ * @param bool $preserveHost Preserve the original state of the Host header.
+ * @return static
+ */
+ public function withUri(UriInterface $uri, $preserveHost = false);
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/ResponseInterface.php b/bin/wiki/vendor/psr/http-message/src/ResponseInterface.php
new file mode 100644
index 00000000..c306514e
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/ResponseInterface.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Representation of an outgoing, server-side response.
+ *
+ * Per the HTTP specification, this interface includes properties for
+ * each of the following:
+ *
+ * - Protocol version
+ * - Status code and reason phrase
+ * - Headers
+ * - Message body
+ *
+ * Responses are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ */
+interface ResponseInterface extends MessageInterface
+{
+ /**
+ * Gets the response status code.
+ *
+ * The status code is a 3-digit integer result code of the server's attempt
+ * to understand and satisfy the request.
+ *
+ * @return int Status code.
+ */
+ public function getStatusCode();
+
+ /**
+ * Return an instance with the specified status code and, optionally, reason phrase.
+ *
+ * If no reason phrase is specified, implementations MAY choose to default
+ * to the RFC 7231 or IANA recommended reason phrase for the response's
+ * status code.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated status and reason phrase.
+ *
+ * @link http://tools.ietf.org/html/rfc7231#section-6
+ * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+ * @param int $code The 3-digit integer result code to set.
+ * @param string $reasonPhrase The reason phrase to use with the
+ * provided status code; if none is provided, implementations MAY
+ * use the defaults as suggested in the HTTP specification.
+ * @return static
+ * @throws \InvalidArgumentException For invalid status code arguments.
+ */
+ public function withStatus($code, $reasonPhrase = '');
+
+ /**
+ * Gets the response reason phrase associated with the status code.
+ *
+ * Because a reason phrase is not a required element in a response
+ * status line, the reason phrase value MAY be null. Implementations MAY
+ * choose to return the default RFC 7231 recommended reason phrase (or those
+ * listed in the IANA HTTP Status Code Registry) for the response's
+ * status code.
+ *
+ * @link http://tools.ietf.org/html/rfc7231#section-6
+ * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+ * @return string Reason phrase; must return an empty string if none present.
+ */
+ public function getReasonPhrase();
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/ServerRequestInterface.php b/bin/wiki/vendor/psr/http-message/src/ServerRequestInterface.php
new file mode 100644
index 00000000..02512340
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/ServerRequestInterface.php
@@ -0,0 +1,261 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Representation of an incoming, server-side HTTP request.
+ *
+ * Per the HTTP specification, this interface includes properties for
+ * each of the following:
+ *
+ * - Protocol version
+ * - HTTP method
+ * - URI
+ * - Headers
+ * - Message body
+ *
+ * Additionally, it encapsulates all data as it has arrived to the
+ * application from the CGI and/or PHP environment, including:
+ *
+ * - The values represented in $_SERVER.
+ * - Any cookies provided (generally via $_COOKIE)
+ * - Query string arguments (generally via $_GET, or as parsed via parse_str())
+ * - Upload files, if any (as represented by $_FILES)
+ * - Deserialized body parameters (generally from $_POST)
+ *
+ * $_SERVER values MUST be treated as immutable, as they represent application
+ * state at the time of request; as such, no methods are provided to allow
+ * modification of those values. The other values provide such methods, as they
+ * can be restored from $_SERVER or the request body, and may need treatment
+ * during the application (e.g., body parameters may be deserialized based on
+ * content type).
+ *
+ * Additionally, this interface recognizes the utility of introspecting a
+ * request to derive and match additional parameters (e.g., via URI path
+ * matching, decrypting cookie values, deserializing non-form-encoded body
+ * content, matching authorization headers to users, etc). These parameters
+ * are stored in an "attributes" property.
+ *
+ * Requests are considered immutable; all methods that might change state MUST
+ * be implemented such that they retain the internal state of the current
+ * message and return an instance that contains the changed state.
+ */
+interface ServerRequestInterface extends RequestInterface
+{
+ /**
+ * Retrieve server parameters.
+ *
+ * Retrieves data related to the incoming request environment,
+ * typically derived from PHP's $_SERVER superglobal. The data IS NOT
+ * REQUIRED to originate from $_SERVER.
+ *
+ * @return array
+ */
+ public function getServerParams();
+
+ /**
+ * Retrieve cookies.
+ *
+ * Retrieves cookies sent by the client to the server.
+ *
+ * The data MUST be compatible with the structure of the $_COOKIE
+ * superglobal.
+ *
+ * @return array
+ */
+ public function getCookieParams();
+
+ /**
+ * Return an instance with the specified cookies.
+ *
+ * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
+ * be compatible with the structure of $_COOKIE. Typically, this data will
+ * be injected at instantiation.
+ *
+ * This method MUST NOT update the related Cookie header of the request
+ * instance, nor related values in the server params.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated cookie values.
+ *
+ * @param array $cookies Array of key/value pairs representing cookies.
+ * @return static
+ */
+ public function withCookieParams(array $cookies);
+
+ /**
+ * Retrieve query string arguments.
+ *
+ * Retrieves the deserialized query string arguments, if any.
+ *
+ * Note: the query params might not be in sync with the URI or server
+ * params. If you need to ensure you are only getting the original
+ * values, you may need to parse the query string from `getUri()->getQuery()`
+ * or from the `QUERY_STRING` server param.
+ *
+ * @return array
+ */
+ public function getQueryParams();
+
+ /**
+ * Return an instance with the specified query string arguments.
+ *
+ * These values SHOULD remain immutable over the course of the incoming
+ * request. They MAY be injected during instantiation, such as from PHP's
+ * $_GET superglobal, or MAY be derived from some other value such as the
+ * URI. In cases where the arguments are parsed from the URI, the data
+ * MUST be compatible with what PHP's parse_str() would return for
+ * purposes of how duplicate query parameters are handled, and how nested
+ * sets are handled.
+ *
+ * Setting query string arguments MUST NOT change the URI stored by the
+ * request, nor the values in the server params.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated query string arguments.
+ *
+ * @param array $query Array of query string arguments, typically from
+ * $_GET.
+ * @return static
+ */
+ public function withQueryParams(array $query);
+
+ /**
+ * Retrieve normalized file upload data.
+ *
+ * This method returns upload metadata in a normalized tree, with each leaf
+ * an instance of Psr\Http\Message\UploadedFileInterface.
+ *
+ * These values MAY be prepared from $_FILES or the message body during
+ * instantiation, or MAY be injected via withUploadedFiles().
+ *
+ * @return array An array tree of UploadedFileInterface instances; an empty
+ * array MUST be returned if no data is present.
+ */
+ public function getUploadedFiles();
+
+ /**
+ * Create a new instance with the specified uploaded files.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated body parameters.
+ *
+ * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
+ * @return static
+ * @throws \InvalidArgumentException if an invalid structure is provided.
+ */
+ public function withUploadedFiles(array $uploadedFiles);
+
+ /**
+ * Retrieve any parameters provided in the request body.
+ *
+ * If the request Content-Type is either application/x-www-form-urlencoded
+ * or multipart/form-data, and the request method is POST, this method MUST
+ * return the contents of $_POST.
+ *
+ * Otherwise, this method may return any results of deserializing
+ * the request body content; as parsing returns structured content, the
+ * potential types MUST be arrays or objects only. A null value indicates
+ * the absence of body content.
+ *
+ * @return null|array|object The deserialized body parameters, if any.
+ * These will typically be an array or object.
+ */
+ public function getParsedBody();
+
+ /**
+ * Return an instance with the specified body parameters.
+ *
+ * These MAY be injected during instantiation.
+ *
+ * If the request Content-Type is either application/x-www-form-urlencoded
+ * or multipart/form-data, and the request method is POST, use this method
+ * ONLY to inject the contents of $_POST.
+ *
+ * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
+ * deserializing the request body content. Deserialization/parsing returns
+ * structured data, and, as such, this method ONLY accepts arrays or objects,
+ * or a null value if nothing was available to parse.
+ *
+ * As an example, if content negotiation determines that the request data
+ * is a JSON payload, this method could be used to create a request
+ * instance with the deserialized parameters.
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated body parameters.
+ *
+ * @param null|array|object $data The deserialized body data. This will
+ * typically be in an array or object.
+ * @return static
+ * @throws \InvalidArgumentException if an unsupported argument type is
+ * provided.
+ */
+ public function withParsedBody($data);
+
+ /**
+ * Retrieve attributes derived from the request.
+ *
+ * The request "attributes" may be used to allow injection of any
+ * parameters derived from the request: e.g., the results of path
+ * match operations; the results of decrypting cookies; the results of
+ * deserializing non-form-encoded message bodies; etc. Attributes
+ * will be application and request specific, and CAN be mutable.
+ *
+ * @return array Attributes derived from the request.
+ */
+ public function getAttributes();
+
+ /**
+ * Retrieve a single derived request attribute.
+ *
+ * Retrieves a single derived request attribute as described in
+ * getAttributes(). If the attribute has not been previously set, returns
+ * the default value as provided.
+ *
+ * This method obviates the need for a hasAttribute() method, as it allows
+ * specifying a default value to return if the attribute is not found.
+ *
+ * @see getAttributes()
+ * @param string $name The attribute name.
+ * @param mixed $default Default value to return if the attribute does not exist.
+ * @return mixed
+ */
+ public function getAttribute($name, $default = null);
+
+ /**
+ * Return an instance with the specified derived request attribute.
+ *
+ * This method allows setting a single derived request attribute as
+ * described in getAttributes().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that has the
+ * updated attribute.
+ *
+ * @see getAttributes()
+ * @param string $name The attribute name.
+ * @param mixed $value The value of the attribute.
+ * @return static
+ */
+ public function withAttribute($name, $value);
+
+ /**
+ * Return an instance that removes the specified derived request attribute.
+ *
+ * This method allows removing a single derived request attribute as
+ * described in getAttributes().
+ *
+ * This method MUST be implemented in such a way as to retain the
+ * immutability of the message, and MUST return an instance that removes
+ * the attribute.
+ *
+ * @see getAttributes()
+ * @param string $name The attribute name.
+ * @return static
+ */
+ public function withoutAttribute($name);
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/StreamInterface.php b/bin/wiki/vendor/psr/http-message/src/StreamInterface.php
new file mode 100644
index 00000000..f68f3912
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/StreamInterface.php
@@ -0,0 +1,158 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Describes a data stream.
+ *
+ * Typically, an instance will wrap a PHP stream; this interface provides
+ * a wrapper around the most common operations, including serialization of
+ * the entire stream to a string.
+ */
+interface StreamInterface
+{
+ /**
+ * Reads all data from the stream into a string, from the beginning to end.
+ *
+ * This method MUST attempt to seek to the beginning of the stream before
+ * reading data and read the stream until the end is reached.
+ *
+ * Warning: This could attempt to load a large amount of data into memory.
+ *
+ * This method MUST NOT raise an exception in order to conform with PHP's
+ * string casting operations.
+ *
+ * @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
+ * @return string
+ */
+ public function __toString();
+
+ /**
+ * Closes the stream and any underlying resources.
+ *
+ * @return void
+ */
+ public function close();
+
+ /**
+ * Separates any underlying resources from the stream.
+ *
+ * After the stream has been detached, the stream is in an unusable state.
+ *
+ * @return resource|null Underlying PHP stream, if any
+ */
+ public function detach();
+
+ /**
+ * Get the size of the stream if known.
+ *
+ * @return int|null Returns the size in bytes if known, or null if unknown.
+ */
+ public function getSize();
+
+ /**
+ * Returns the current position of the file read/write pointer
+ *
+ * @return int Position of the file pointer
+ * @throws \RuntimeException on error.
+ */
+ public function tell();
+
+ /**
+ * Returns true if the stream is at the end of the stream.
+ *
+ * @return bool
+ */
+ public function eof();
+
+ /**
+ * Returns whether or not the stream is seekable.
+ *
+ * @return bool
+ */
+ public function isSeekable();
+
+ /**
+ * Seek to a position in the stream.
+ *
+ * @link http://www.php.net/manual/en/function.fseek.php
+ * @param int $offset Stream offset
+ * @param int $whence Specifies how the cursor position will be calculated
+ * based on the seek offset. Valid values are identical to the built-in
+ * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
+ * offset bytes SEEK_CUR: Set position to current location plus offset
+ * SEEK_END: Set position to end-of-stream plus offset.
+ * @throws \RuntimeException on failure.
+ */
+ public function seek($offset, $whence = SEEK_SET);
+
+ /**
+ * Seek to the beginning of the stream.
+ *
+ * If the stream is not seekable, this method will raise an exception;
+ * otherwise, it will perform a seek(0).
+ *
+ * @see seek()
+ * @link http://www.php.net/manual/en/function.fseek.php
+ * @throws \RuntimeException on failure.
+ */
+ public function rewind();
+
+ /**
+ * Returns whether or not the stream is writable.
+ *
+ * @return bool
+ */
+ public function isWritable();
+
+ /**
+ * Write data to the stream.
+ *
+ * @param string $string The string that is to be written.
+ * @return int Returns the number of bytes written to the stream.
+ * @throws \RuntimeException on failure.
+ */
+ public function write($string);
+
+ /**
+ * Returns whether or not the stream is readable.
+ *
+ * @return bool
+ */
+ public function isReadable();
+
+ /**
+ * Read data from the stream.
+ *
+ * @param int $length Read up to $length bytes from the object and return
+ * them. Fewer than $length bytes may be returned if underlying stream
+ * call returns fewer bytes.
+ * @return string Returns the data read from the stream, or an empty string
+ * if no bytes are available.
+ * @throws \RuntimeException if an error occurs.
+ */
+ public function read($length);
+
+ /**
+ * Returns the remaining contents in a string
+ *
+ * @return string
+ * @throws \RuntimeException if unable to read or an error occurs while
+ * reading.
+ */
+ public function getContents();
+
+ /**
+ * Get stream metadata as an associative array or retrieve a specific key.
+ *
+ * The keys returned are identical to the keys returned from PHP's
+ * stream_get_meta_data() function.
+ *
+ * @link http://php.net/manual/en/function.stream-get-meta-data.php
+ * @param string $key Specific metadata to retrieve.
+ * @return array|mixed|null Returns an associative array if no key is
+ * provided. Returns a specific key value if a key is provided and the
+ * value is found, or null if the key is not found.
+ */
+ public function getMetadata($key = null);
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/UploadedFileInterface.php b/bin/wiki/vendor/psr/http-message/src/UploadedFileInterface.php
new file mode 100644
index 00000000..f8a6901e
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/UploadedFileInterface.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Psr\Http\Message;
+
+/**
+ * Value object representing a file uploaded through an HTTP request.
+ *
+ * Instances of this interface are considered immutable; all methods that
+ * might change state MUST be implemented such that they retain the internal
+ * state of the current instance and return an instance that contains the
+ * changed state.
+ */
+interface UploadedFileInterface
+{
+ /**
+ * Retrieve a stream representing the uploaded file.
+ *
+ * This method MUST return a StreamInterface instance, representing the
+ * uploaded file. The purpose of this method is to allow utilizing native PHP
+ * stream functionality to manipulate the file upload, such as
+ * stream_copy_to_stream() (though the result will need to be decorated in a
+ * native PHP stream wrapper to work with such functions).
+ *
+ * If the moveTo() method has been called previously, this method MUST raise
+ * an exception.
+ *
+ * @return StreamInterface Stream representation of the uploaded file.
+ * @throws \RuntimeException in cases when no stream is available or can be
+ * created.
+ */
+ public function getStream();
+
+ /**
+ * Move the uploaded file to a new location.
+ *
+ * Use this method as an alternative to move_uploaded_file(). This method is
+ * guaranteed to work in both SAPI and non-SAPI environments.
+ * Implementations must determine which environment they are in, and use the
+ * appropriate method (move_uploaded_file(), rename(), or a stream
+ * operation) to perform the operation.
+ *
+ * $targetPath may be an absolute path, or a relative path. If it is a
+ * relative path, resolution should be the same as used by PHP's rename()
+ * function.
+ *
+ * The original file or stream MUST be removed on completion.
+ *
+ * If this method is called more than once, any subsequent calls MUST raise
+ * an exception.
+ *
+ * When used in an SAPI environment where $_FILES is populated, when writing
+ * files via moveTo(), is_uploaded_file() and move_uploaded_file() SHOULD be
+ * used to ensure permissions and upload status are verified correctly.
+ *
+ * If you wish to move to a stream, use getStream(), as SAPI operations
+ * cannot guarantee writing to stream destinations.
+ *
+ * @see http://php.net/is_uploaded_file
+ * @see http://php.net/move_uploaded_file
+ * @param string $targetPath Path to which to move the uploaded file.
+ * @throws \InvalidArgumentException if the $targetPath specified is invalid.
+ * @throws \RuntimeException on any error during the move operation, or on
+ * the second or subsequent call to the method.
+ */
+ public function moveTo($targetPath);
+
+ /**
+ * Retrieve the file size.
+ *
+ * Implementations SHOULD return the value stored in the "size" key of
+ * the file in the $_FILES array if available, as PHP calculates this based
+ * on the actual size transmitted.
+ *
+ * @return int|null The file size in bytes or null if unknown.
+ */
+ public function getSize();
+
+ /**
+ * Retrieve the error associated with the uploaded file.
+ *
+ * The return value MUST be one of PHP's UPLOAD_ERR_XXX constants.
+ *
+ * If the file was uploaded successfully, this method MUST return
+ * UPLOAD_ERR_OK.
+ *
+ * Implementations SHOULD return the value stored in the "error" key of
+ * the file in the $_FILES array.
+ *
+ * @see http://php.net/manual/en/features.file-upload.errors.php
+ * @return int One of PHP's UPLOAD_ERR_XXX constants.
+ */
+ public function getError();
+
+ /**
+ * Retrieve the filename sent by the client.
+ *
+ * Do not trust the value returned by this method. A client could send
+ * a malicious filename with the intention to corrupt or hack your
+ * application.
+ *
+ * Implementations SHOULD return the value stored in the "name" key of
+ * the file in the $_FILES array.
+ *
+ * @return string|null The filename sent by the client or null if none
+ * was provided.
+ */
+ public function getClientFilename();
+
+ /**
+ * Retrieve the media type sent by the client.
+ *
+ * Do not trust the value returned by this method. A client could send
+ * a malicious media type with the intention to corrupt or hack your
+ * application.
+ *
+ * Implementations SHOULD return the value stored in the "type" key of
+ * the file in the $_FILES array.
+ *
+ * @return string|null The media type sent by the client or null if none
+ * was provided.
+ */
+ public function getClientMediaType();
+}
diff --git a/bin/wiki/vendor/psr/http-message/src/UriInterface.php b/bin/wiki/vendor/psr/http-message/src/UriInterface.php
new file mode 100644
index 00000000..9d7ab9ea
--- /dev/null
+++ b/bin/wiki/vendor/psr/http-message/src/UriInterface.php
@@ -0,0 +1,323 @@
+<?php
+namespace Psr\Http\Message;
+
+/**
+ * Value object representing a URI.
+ *
+ * This interface is meant to represent URIs according to RFC 3986 and to
+ * provide methods for most common operations. Additional functionality for
+ * working with URIs can be provided on top of the interface or externally.
+ * Its primary use is for HTTP requests, but may also be used in other
+ * contexts.
+ *
+ * Instances of this interface are considered immutable; all methods that
+ * might change state MUST be implemented such that they retain the internal
+ * state of the current instance and return an instance that contains the
+ * changed state.
+ *
+ * Typically the Host header will be also be present in the request message.
+ * For server-side requests, the scheme will typically be discoverable in the
+ * server parameters.
+ *
+ * @link http://tools.ietf.org/html/rfc3986 (the URI specification)
+ */
+interface UriInterface
+{
+ /**
+ * Retrieve the scheme component of the URI.
+ *
+ * If no scheme is present, this method MUST return an empty string.
+ *
+ * The value returned MUST be normalized to lowercase, per RFC 3986
+ * Section 3.1.
+ *
+ * The trailing ":" character is not part of the scheme and MUST NOT be
+ * added.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.1
+ * @return string The URI scheme.
+ */
+ public function getScheme();
+
+ /**
+ * Retrieve the authority component of the URI.
+ *
+ * If no authority information is present, this method MUST return an empty
+ * string.
+ *
+ * The authority syntax of the URI is:
+ *
+ * <pre>
+ * [user-info@]host[:port]
+ * </pre>
+ *
+ * If the port component is not set or is the standard port for the current
+ * scheme, it SHOULD NOT be included.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-3.2
+ * @return string The URI authority, in "[user-info@]host[:port]" format.
+ */
+ public function getAuthority();
+
+ /**
+ * Retrieve the user information component of the URI.
+ *
+ * If no user information is present, this method MUST return an empty
+ * string.
+ *
+ * If a user is present in the URI, this will return that value;
+ * additionally, if the password is also present, it will be appended to the
+ * user value, with a colon (":") separating the values.
+ *
+ * The trailing "@" character is not part of the user information and MUST
+ * NOT be added.
+ *
+ * @return string The URI user information, in "username[:password]" format.
+ */
+ public function getUserInfo();
+
+ /**
+ * Retrieve the host component of the URI.
+ *
+ * If no host is present, this method MUST return an empty string.
+ *
+ * The value returned MUST be normalized to lowercase, per RFC 3986
+ * Section 3.2.2.
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
+ * @return string The URI host.
+ */
+ public function getHost();
+
+ /**
+ * Retrieve the port component of the URI.
+ *
+ * If a port is present, and it is non-standard for the current scheme,
+ * this method MUST return it as an integer. If the port is the standard port
+ * used with the current scheme, this method SHOULD return null.
+ *
+ * If no port is present, and no scheme is present, this method MUST return
+ * a null value.
+ *
+ * If no port is present, but a scheme is present, this method MAY return
+ * the standard port for that scheme, but SHOULD return null.
+ *
+ * @return null|int The URI port.
+ */
+ public function getPort();
+
+ /**
+ * Retrieve the path component of the URI.
+ *
+ * The path can either be empty or absolute (starting with a slash) or
+ * rootless (not starting with a slash). Implementations MUST support all
+ * three syntaxes.
+ *
+ * Normally, the empty path "" and absolute path "/" are considered equal as
+ * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
+ * do this normalization because in contexts with a trimmed base path, e.g.
+ * the front controller, this difference becomes significant. It's the task
+ * of the user to handle both "" and "/".
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.3.
+ *
+ * As an example, if the value should include a slash ("/") not intended as
+ * delimiter between path segments, that value MUST be passed in encoded
+ * form (e.g., "%2F") to the instance.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.3
+ * @return string The URI path.
+ */
+ public function getPath();
+
+ /**
+ * Retrieve the query string of the URI.
+ *
+ * If no query string is present, this method MUST return an empty string.
+ *
+ * The leading "?" character is not part of the query and MUST NOT be
+ * added.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.4.
+ *
+ * As an example, if a value in a key/value pair of the query string should
+ * include an ampersand ("&") not intended as a delimiter between values,
+ * that value MUST be passed in encoded form (e.g., "%26") to the instance.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.4
+ * @return string The URI query string.
+ */
+ public function getQuery();
+
+ /**
+ * Retrieve the fragment component of the URI.
+ *
+ * If no fragment is present, this method MUST return an empty string.
+ *
+ * The leading "#" character is not part of the fragment and MUST NOT be
+ * added.
+ *
+ * The value returned MUST be percent-encoded, but MUST NOT double-encode
+ * any characters. To determine what characters to encode, please refer to
+ * RFC 3986, Sections 2 and 3.5.
+ *
+ * @see https://tools.ietf.org/html/rfc3986#section-2
+ * @see https://tools.ietf.org/html/rfc3986#section-3.5
+ * @return string The URI fragment.
+ */
+ public function getFragment();
+
+ /**
+ * Return an instance with the specified scheme.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified scheme.
+ *
+ * Implementations MUST support the schemes "http" and "https" case
+ * insensitively, and MAY accommodate other schemes if required.
+ *
+ * An empty scheme is equivalent to removing the scheme.
+ *
+ * @param string $scheme The scheme to use with the new instance.
+ * @return static A new instance with the specified scheme.
+ * @throws \InvalidArgumentException for invalid or unsupported schemes.
+ */
+ public function withScheme($scheme);
+
+ /**
+ * Return an instance with the specified user information.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified user information.
+ *
+ * Password is optional, but the user information MUST include the
+ * user; an empty string for the user is equivalent to removing user
+ * information.
+ *
+ * @param string $user The user name to use for authority.
+ * @param null|string $password The password associated with $user.
+ * @return static A new instance with the specified user information.
+ */
+ public function withUserInfo($user, $password = null);
+
+ /**
+ * Return an instance with the specified host.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified host.
+ *
+ * An empty host value is equivalent to removing the host.
+ *
+ * @param string $host The hostname to use with the new instance.
+ * @return static A new instance with the specified host.
+ * @throws \InvalidArgumentException for invalid hostnames.
+ */
+ public function withHost($host);
+
+ /**
+ * Return an instance with the specified port.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified port.
+ *
+ * Implementations MUST raise an exception for ports outside the
+ * established TCP and UDP port ranges.
+ *
+ * A null value provided for the port is equivalent to removing the port
+ * information.
+ *
+ * @param null|int $port The port to use with the new instance; a null value
+ * removes the port information.
+ * @return static A new instance with the specified port.
+ * @throws \InvalidArgumentException for invalid ports.
+ */
+ public function withPort($port);
+
+ /**
+ * Return an instance with the specified path.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified path.
+ *
+ * The path can either be empty or absolute (starting with a slash) or
+ * rootless (not starting with a slash). Implementations MUST support all
+ * three syntaxes.
+ *
+ * If the path is intended to be domain-relative rather than path relative then
+ * it must begin with a slash ("/"). Paths not starting with a slash ("/")
+ * are assumed to be relative to some base path known to the application or
+ * consumer.
+ *
+ * Users can provide both encoded and decoded path characters.
+ * Implementations ensure the correct encoding as outlined in getPath().
+ *
+ * @param string $path The path to use with the new instance.
+ * @return static A new instance with the specified path.
+ * @throws \InvalidArgumentException for invalid paths.
+ */
+ public function withPath($path);
+
+ /**
+ * Return an instance with the specified query string.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified query string.
+ *
+ * Users can provide both encoded and decoded query characters.
+ * Implementations ensure the correct encoding as outlined in getQuery().
+ *
+ * An empty query string value is equivalent to removing the query string.
+ *
+ * @param string $query The query string to use with the new instance.
+ * @return static A new instance with the specified query string.
+ * @throws \InvalidArgumentException for invalid query strings.
+ */
+ public function withQuery($query);
+
+ /**
+ * Return an instance with the specified URI fragment.
+ *
+ * This method MUST retain the state of the current instance, and return
+ * an instance that contains the specified URI fragment.
+ *
+ * Users can provide both encoded and decoded fragment characters.
+ * Implementations ensure the correct encoding as outlined in getFragment().
+ *
+ * An empty fragment value is equivalent to removing the fragment.
+ *
+ * @param string $fragment The fragment to use with the new instance.
+ * @return static A new instance with the specified fragment.
+ */
+ public function withFragment($fragment);
+
+ /**
+ * Return the string representation as a URI reference.
+ *
+ * Depending on which components of the URI are present, the resulting
+ * string is either a full URI or relative reference according to RFC 3986,
+ * Section 4.1. The method concatenates the various components of the URI,
+ * using the appropriate delimiters:
+ *
+ * - If a scheme is present, it MUST be suffixed by ":".
+ * - If an authority is present, it MUST be prefixed by "//".
+ * - The path can be concatenated without delimiters. But there are two
+ * cases where the path has to be adjusted to make the URI reference
+ * valid as PHP does not allow to throw an exception in __toString():
+ * - If the path is rootless and an authority is present, the path MUST
+ * be prefixed by "/".
+ * - If the path is starting with more than one "/" and no authority is
+ * present, the starting slashes MUST be reduced to one.
+ * - If a query is present, it MUST be prefixed by "?".
+ * - If a fragment is present, it MUST be prefixed by "#".
+ *
+ * @see http://tools.ietf.org/html/rfc3986#section-4.1
+ * @return string
+ */
+ public function __toString();
+}
diff --git a/bin/wiki/vendor/psr/log/.gitignore b/bin/wiki/vendor/psr/log/.gitignore
new file mode 100644
index 00000000..22d0d82f
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/.gitignore
@@ -0,0 +1 @@
+vendor
diff --git a/bin/wiki/vendor/psr/log/LICENSE b/bin/wiki/vendor/psr/log/LICENSE
new file mode 100644
index 00000000..474c952b
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 PHP Framework Interoperability Group
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/AbstractLogger.php b/bin/wiki/vendor/psr/log/Psr/Log/AbstractLogger.php
new file mode 100644
index 00000000..90e721af
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/AbstractLogger.php
@@ -0,0 +1,128 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This is a simple Logger implementation that other Loggers can inherit from.
+ *
+ * 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 AbstractLogger implements LoggerInterface
+{
+ /**
+ * System is unusable.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function emergency($message, array $context = array())
+ {
+ $this->log(LogLevel::EMERGENCY, $message, $context);
+ }
+
+ /**
+ * Action must be taken immediately.
+ *
+ * Example: Entire website down, database unavailable, etc. This should
+ * trigger the SMS alerts and wake you up.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function alert($message, array $context = array())
+ {
+ $this->log(LogLevel::ALERT, $message, $context);
+ }
+
+ /**
+ * Critical conditions.
+ *
+ * Example: Application component unavailable, unexpected exception.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function critical($message, array $context = array())
+ {
+ $this->log(LogLevel::CRITICAL, $message, $context);
+ }
+
+ /**
+ * Runtime errors that do not require immediate action but should typically
+ * be logged and monitored.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function error($message, array $context = array())
+ {
+ $this->log(LogLevel::ERROR, $message, $context);
+ }
+
+ /**
+ * Exceptional occurrences that are not errors.
+ *
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
+ * that are not necessarily wrong.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function warning($message, array $context = array())
+ {
+ $this->log(LogLevel::WARNING, $message, $context);
+ }
+
+ /**
+ * Normal but significant events.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function notice($message, array $context = array())
+ {
+ $this->log(LogLevel::NOTICE, $message, $context);
+ }
+
+ /**
+ * Interesting events.
+ *
+ * Example: User logs in, SQL logs.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function info($message, array $context = array())
+ {
+ $this->log(LogLevel::INFO, $message, $context);
+ }
+
+ /**
+ * Detailed debug information.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function debug($message, array $context = array())
+ {
+ $this->log(LogLevel::DEBUG, $message, $context);
+ }
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/bin/wiki/vendor/psr/log/Psr/Log/InvalidArgumentException.php
new file mode 100644
index 00000000..67f852d1
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/InvalidArgumentException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Psr\Log;
+
+class InvalidArgumentException extends \InvalidArgumentException
+{
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/LogLevel.php b/bin/wiki/vendor/psr/log/Psr/Log/LogLevel.php
new file mode 100644
index 00000000..9cebcace
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/LogLevel.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes log levels.
+ */
+class LogLevel
+{
+ const EMERGENCY = 'emergency';
+ const ALERT = 'alert';
+ const CRITICAL = 'critical';
+ const ERROR = 'error';
+ const WARNING = 'warning';
+ const NOTICE = 'notice';
+ const INFO = 'info';
+ const DEBUG = 'debug';
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareInterface.php b/bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareInterface.php
new file mode 100644
index 00000000..4d64f478
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes a logger-aware instance.
+ */
+interface LoggerAwareInterface
+{
+ /**
+ * Sets a logger instance on the object.
+ *
+ * @param LoggerInterface $logger
+ *
+ * @return void
+ */
+ public function setLogger(LoggerInterface $logger);
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareTrait.php b/bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareTrait.php
new file mode 100644
index 00000000..639f79bd
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/LoggerAwareTrait.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Basic Implementation of LoggerAwareInterface.
+ */
+trait LoggerAwareTrait
+{
+ /**
+ * The logger instance.
+ *
+ * @var LoggerInterface
+ */
+ protected $logger;
+
+ /**
+ * Sets a logger.
+ *
+ * @param LoggerInterface $logger
+ */
+ public function setLogger(LoggerInterface $logger)
+ {
+ $this->logger = $logger;
+ }
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/LoggerInterface.php b/bin/wiki/vendor/psr/log/Psr/Log/LoggerInterface.php
new file mode 100644
index 00000000..5ea72438
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/LoggerInterface.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * Describes a logger instance.
+ *
+ * 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 LoggerInterface
+{
+ /**
+ * System is unusable.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ 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
+ *
+ * @return void
+ */
+ public function alert($message, array $context = array());
+
+ /**
+ * Critical conditions.
+ *
+ * Example: Application component unavailable, unexpected exception.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ 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
+ *
+ * @return void
+ */
+ 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
+ *
+ * @return void
+ */
+ public function warning($message, array $context = array());
+
+ /**
+ * Normal but significant events.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function notice($message, array $context = array());
+
+ /**
+ * Interesting events.
+ *
+ * Example: User logs in, SQL logs.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function info($message, array $context = array());
+
+ /**
+ * Detailed debug information.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function debug($message, array $context = array());
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function log($level, $message, array $context = array());
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/LoggerTrait.php b/bin/wiki/vendor/psr/log/Psr/Log/LoggerTrait.php
new file mode 100644
index 00000000..867225df
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/LoggerTrait.php
@@ -0,0 +1,140 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This is a simple Logger trait that classes unable to extend AbstractLogger
+ * (because they extend another class, etc) can include.
+ *
+ * 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.
+ */
+trait LoggerTrait
+{
+ /**
+ * System is unusable.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function emergency($message, array $context = array())
+ {
+ $this->log(LogLevel::EMERGENCY, $message, $context);
+ }
+
+ /**
+ * Action must be taken immediately.
+ *
+ * Example: Entire website down, database unavailable, etc. This should
+ * trigger the SMS alerts and wake you up.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function alert($message, array $context = array())
+ {
+ $this->log(LogLevel::ALERT, $message, $context);
+ }
+
+ /**
+ * Critical conditions.
+ *
+ * Example: Application component unavailable, unexpected exception.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function critical($message, array $context = array())
+ {
+ $this->log(LogLevel::CRITICAL, $message, $context);
+ }
+
+ /**
+ * Runtime errors that do not require immediate action but should typically
+ * be logged and monitored.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function error($message, array $context = array())
+ {
+ $this->log(LogLevel::ERROR, $message, $context);
+ }
+
+ /**
+ * Exceptional occurrences that are not errors.
+ *
+ * Example: Use of deprecated APIs, poor use of an API, undesirable things
+ * that are not necessarily wrong.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function warning($message, array $context = array())
+ {
+ $this->log(LogLevel::WARNING, $message, $context);
+ }
+
+ /**
+ * Normal but significant events.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function notice($message, array $context = array())
+ {
+ $this->log(LogLevel::NOTICE, $message, $context);
+ }
+
+ /**
+ * Interesting events.
+ *
+ * Example: User logs in, SQL logs.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function info($message, array $context = array())
+ {
+ $this->log(LogLevel::INFO, $message, $context);
+ }
+
+ /**
+ * Detailed debug information.
+ *
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function debug($message, array $context = array())
+ {
+ $this->log(LogLevel::DEBUG, $message, $context);
+ }
+
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ abstract public function log($level, $message, array $context = array());
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/NullLogger.php b/bin/wiki/vendor/psr/log/Psr/Log/NullLogger.php
new file mode 100644
index 00000000..d8cd682c
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/NullLogger.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Psr\Log;
+
+/**
+ * This Logger can be used to avoid conditional log calls.
+ *
+ * Logging should always be optional, and if no logger is provided to your
+ * library creating a NullLogger instance to have something to throw logs at
+ * is a good way to avoid littering your code with `if ($this->logger) { }`
+ * blocks.
+ */
+class NullLogger extends AbstractLogger
+{
+ /**
+ * Logs with an arbitrary level.
+ *
+ * @param mixed $level
+ * @param string $message
+ * @param array $context
+ *
+ * @return void
+ */
+ public function log($level, $message, array $context = array())
+ {
+ // noop
+ }
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/bin/wiki/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
new file mode 100644
index 00000000..4b861c3e
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php
@@ -0,0 +1,144 @@
+<?php
+
+namespace Psr\Log\Test;
+
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+/**
+ * Provides a base test class for ensuring compliance with the LoggerInterface.
+ *
+ * Implementors can extend the class and implement abstract methods to run this
+ * as part of their test suite.
+ */
+abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @return LoggerInterface
+ */
+ abstract public function getLogger();
+
+ /**
+ * This must return the log messages in order.
+ *
+ * The simple formatting of the messages is: "<LOG LEVEL> <MESSAGE>".
+ *
+ * Example ->error('Foo') would yield "error Foo".
+ *
+ * @return string[]
+ */
+ abstract public function getLogs();
+
+ public function testImplements()
+ {
+ $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger());
+ }
+
+ /**
+ * @dataProvider provideLevelsAndMessages
+ */
+ public function testLogsAtAllLevels($level, $message)
+ {
+ $logger = $this->getLogger();
+ $logger->{$level}($message, array('user' => 'Bob'));
+ $logger->log($level, $message, array('user' => 'Bob'));
+
+ $expected = array(
+ $level.' message of level '.$level.' with context: Bob',
+ $level.' message of level '.$level.' with context: Bob',
+ );
+ $this->assertEquals($expected, $this->getLogs());
+ }
+
+ public function provideLevelsAndMessages()
+ {
+ return array(
+ LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'),
+ LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'),
+ LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'),
+ LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'),
+ LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'),
+ LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'),
+ LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'),
+ LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'),
+ );
+ }
+
+ /**
+ * @expectedException \Psr\Log\InvalidArgumentException
+ */
+ public function testThrowsOnInvalidLevel()
+ {
+ $logger = $this->getLogger();
+ $logger->log('invalid level', 'Foo');
+ }
+
+ public function testContextReplacement()
+ {
+ $logger = $this->getLogger();
+ $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar'));
+
+ $expected = array('info {Message {nothing} Bob Bar a}');
+ $this->assertEquals($expected, $this->getLogs());
+ }
+
+ public function testObjectCastToString()
+ {
+ if (method_exists($this, 'createPartialMock')) {
+ $dummy = $this->createPartialMock('Psr\Log\Test\DummyTest', array('__toString'));
+ } else {
+ $dummy = $this->getMock('Psr\Log\Test\DummyTest', array('__toString'));
+ }
+ $dummy->expects($this->once())
+ ->method('__toString')
+ ->will($this->returnValue('DUMMY'));
+
+ $this->getLogger()->warning($dummy);
+
+ $expected = array('warning DUMMY');
+ $this->assertEquals($expected, $this->getLogs());
+ }
+
+ public function testContextCanContainAnything()
+ {
+ $closed = fopen('php://memory', 'r');
+ fclose($closed);
+
+ $context = array(
+ 'bool' => true,
+ 'null' => null,
+ 'string' => 'Foo',
+ 'int' => 0,
+ 'float' => 0.5,
+ 'nested' => array('with object' => new DummyTest),
+ 'object' => new \DateTime,
+ 'resource' => fopen('php://memory', 'r'),
+ 'closed' => $closed,
+ );
+
+ $this->getLogger()->warning('Crazy context data', $context);
+
+ $expected = array('warning Crazy context data');
+ $this->assertEquals($expected, $this->getLogs());
+ }
+
+ public function testContextExceptionKeyCanBeExceptionOrOtherValues()
+ {
+ $logger = $this->getLogger();
+ $logger->warning('Random message', array('exception' => 'oops'));
+ $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail')));
+
+ $expected = array(
+ 'warning Random message',
+ 'critical Uncaught Exception!'
+ );
+ $this->assertEquals($expected, $this->getLogs());
+ }
+}
+
+class DummyTest
+{
+ public function __toString()
+ {
+ }
+}
diff --git a/bin/wiki/vendor/psr/log/Psr/Log/Test/TestLogger.php b/bin/wiki/vendor/psr/log/Psr/Log/Test/TestLogger.php
new file mode 100644
index 00000000..0cdffe4f
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/Psr/Log/Test/TestLogger.php
@@ -0,0 +1,146 @@
+<?php
+
+namespace Psr\Log\Test;
+
+use Psr\Log\AbstractLogger;
+
+/**
+ * Used for testing purposes.
+ *
+ * It records all records and gives you access to them for verification.
+ *
+ * @method bool hasEmergency($record)
+ * @method bool hasAlert($record)
+ * @method bool hasCritical($record)
+ * @method bool hasError($record)
+ * @method bool hasWarning($record)
+ * @method bool hasNotice($record)
+ * @method bool hasInfo($record)
+ * @method bool hasDebug($record)
+ *
+ * @method bool hasEmergencyRecords()
+ * @method bool hasAlertRecords()
+ * @method bool hasCriticalRecords()
+ * @method bool hasErrorRecords()
+ * @method bool hasWarningRecords()
+ * @method bool hasNoticeRecords()
+ * @method bool hasInfoRecords()
+ * @method bool hasDebugRecords()
+ *
+ * @method bool hasEmergencyThatContains($message)
+ * @method bool hasAlertThatContains($message)
+ * @method bool hasCriticalThatContains($message)
+ * @method bool hasErrorThatContains($message)
+ * @method bool hasWarningThatContains($message)
+ * @method bool hasNoticeThatContains($message)
+ * @method bool hasInfoThatContains($message)
+ * @method bool hasDebugThatContains($message)
+ *
+ * @method bool hasEmergencyThatMatches($message)
+ * @method bool hasAlertThatMatches($message)
+ * @method bool hasCriticalThatMatches($message)
+ * @method bool hasErrorThatMatches($message)
+ * @method bool hasWarningThatMatches($message)
+ * @method bool hasNoticeThatMatches($message)
+ * @method bool hasInfoThatMatches($message)
+ * @method bool hasDebugThatMatches($message)
+ *
+ * @method bool hasEmergencyThatPasses($message)
+ * @method bool hasAlertThatPasses($message)
+ * @method bool hasCriticalThatPasses($message)
+ * @method bool hasErrorThatPasses($message)
+ * @method bool hasWarningThatPasses($message)
+ * @method bool hasNoticeThatPasses($message)
+ * @method bool hasInfoThatPasses($message)
+ * @method bool hasDebugThatPasses($message)
+ */
+class TestLogger extends AbstractLogger
+{
+ /**
+ * @var array
+ */
+ public $records = [];
+
+ public $recordsByLevel = [];
+
+ /**
+ * @inheritdoc
+ */
+ public function log($level, $message, array $context = [])
+ {
+ $record = [
+ 'level' => $level,
+ 'message' => $message,
+ 'context' => $context,
+ ];
+
+ $this->recordsByLevel[$record['level']][] = $record;
+ $this->records[] = $record;
+ }
+
+ public function hasRecords($level)
+ {
+ return isset($this->recordsByLevel[$level]);
+ }
+
+ public function hasRecord($record, $level)
+ {
+ if (is_string($record)) {
+ $record = ['message' => $record];
+ }
+ return $this->hasRecordThatPasses(function ($rec) use ($record) {
+ if ($rec['message'] !== $record['message']) {
+ return false;
+ }
+ if (isset($record['context']) && $rec['context'] !== $record['context']) {
+ return false;
+ }
+ return true;
+ }, $level);
+ }
+
+ public function hasRecordThatContains($message, $level)
+ {
+ return $this->hasRecordThatPasses(function ($rec) use ($message) {
+ return strpos($rec['message'], $message) !== false;
+ }, $level);
+ }
+
+ public function hasRecordThatMatches($regex, $level)
+ {
+ return $this->hasRecordThatPasses(function ($rec) use ($regex) {
+ return preg_match($regex, $rec['message']) > 0;
+ }, $level);
+ }
+
+ public function hasRecordThatPasses(callable $predicate, $level)
+ {
+ if (!isset($this->recordsByLevel[$level])) {
+ return false;
+ }
+ foreach ($this->recordsByLevel[$level] as $i => $rec) {
+ if (call_user_func($predicate, $rec, $i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public function __call($method, $args)
+ {
+ if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) {
+ $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3];
+ $level = strtolower($matches[2]);
+ if (method_exists($this, $genericMethod)) {
+ $args[] = $level;
+ return call_user_func_array([$this, $genericMethod], $args);
+ }
+ }
+ throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()');
+ }
+
+ public function reset()
+ {
+ $this->records = [];
+ }
+}
diff --git a/bin/wiki/vendor/psr/log/README.md b/bin/wiki/vendor/psr/log/README.md
new file mode 100644
index 00000000..5571a25e
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/README.md
@@ -0,0 +1,52 @@
+PSR Log
+=======
+
+This repository holds all interfaces/classes/traits related to
+[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md).
+
+Note that this is not a logger of its own. It is merely an interface that
+describes a logger. See the specification for more details.
+
+Installation
+------------
+
+```bash
+composer require psr/log
+```
+
+Usage
+-----
+
+If you need a logger, you can use the interface like this:
+
+```php
+<?php
+
+use Psr\Log\LoggerInterface;
+
+class Foo
+{
+ private $logger;
+
+ public function __construct(LoggerInterface $logger = null)
+ {
+ $this->logger = $logger;
+ }
+
+ public function doSomething()
+ {
+ if ($this->logger) {
+ $this->logger->info('Doing work');
+ }
+
+ // do something useful
+ }
+}
+```
+
+You can then pick one of the implementations of the interface to get a logger.
+
+If you want to implement the interface, you can require this package and
+implement `Psr\Log\LoggerInterface` in your code. Please read the
+[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md)
+for details.
diff --git a/bin/wiki/vendor/psr/log/composer.json b/bin/wiki/vendor/psr/log/composer.json
new file mode 100644
index 00000000..87934d70
--- /dev/null
+++ b/bin/wiki/vendor/psr/log/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "psr/log",
+ "description": "Common interface for logging libraries",
+ "keywords": ["psr", "psr-3", "log"],
+ "homepage": "https://github.com/php-fig/log",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ }
+}
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/.gitignore b/bin/wiki/vendor/ralouphie/getallheaders/.gitignore
new file mode 100644
index 00000000..1324e7d3
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/.gitignore
@@ -0,0 +1,5 @@
+.idea
+.DS_store
+/vendor/
+composer.phar
+composer.lock
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/.travis.yml b/bin/wiki/vendor/ralouphie/getallheaders/.travis.yml
new file mode 100644
index 00000000..f45b55fa
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/.travis.yml
@@ -0,0 +1,18 @@
+language: php
+
+php:
+ - 5.3
+ - 5.4
+ - 5.5
+ - 5.6
+ - 7.0
+
+before_script:
+ - composer install
+
+script:
+ - mkdir -p build/logs
+ - php vendor/bin/phpunit -c phpunit.xml
+
+after_script:
+ - php vendor/bin/coveralls -v \ No newline at end of file
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/LICENSE b/bin/wiki/vendor/ralouphie/getallheaders/LICENSE
new file mode 100644
index 00000000..be5540c2
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ralph Khattar
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/README.md b/bin/wiki/vendor/ralouphie/getallheaders/README.md
new file mode 100644
index 00000000..f3329d66
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/README.md
@@ -0,0 +1,19 @@
+getallheaders
+=============
+
+PHP `getallheaders()` polyfill. Compatible with PHP >= 5.3.
+
+[![Build Status](https://travis-ci.org/ralouphie/getallheaders.svg?branch=master)](https://travis-ci.org/ralouphie/getallheaders)
+[![Coverage Status](https://coveralls.io/repos/ralouphie/getallheaders/badge.png?branch=master)](https://coveralls.io/r/ralouphie/getallheaders?branch=master)
+[![Latest Stable Version](https://poser.pugx.org/ralouphie/getallheaders/v/stable.png)](https://packagist.org/packages/ralouphie/getallheaders)
+[![Latest Unstable Version](https://poser.pugx.org/ralouphie/getallheaders/v/unstable.png)](https://packagist.org/packages/ralouphie/getallheaders)
+[![License](https://poser.pugx.org/ralouphie/getallheaders/license.png)](https://packagist.org/packages/ralouphie/getallheaders)
+
+
+This is a simple polyfill for [`getallheaders()`](http://www.php.net/manual/en/function.getallheaders.php).
+
+## Install
+
+```
+composer require ralouphie/getallheaders
+```
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/composer.json b/bin/wiki/vendor/ralouphie/getallheaders/composer.json
new file mode 100644
index 00000000..5a0d595c
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/composer.json
@@ -0,0 +1,21 @@
+{
+ "name": "ralouphie/getallheaders",
+ "description": "A polyfill for getallheaders.",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~3.7.0",
+ "satooshi/php-coveralls": ">=1.0"
+ },
+ "autoload": {
+ "files": ["src/getallheaders.php"]
+ }
+} \ No newline at end of file
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/phpunit.xml b/bin/wiki/vendor/ralouphie/getallheaders/phpunit.xml
new file mode 100644
index 00000000..7255b23d
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/phpunit.xml
@@ -0,0 +1,22 @@
+<phpunit
+ bootstrap="vendor/autoload.php"
+ convertErrorsToExceptions="true"
+ convertNoticesToExceptions="true"
+ convertWarningsToExceptions="true"
+ strict="true">
+
+ <testsuite>
+ <directory>./tests</directory>
+ </testsuite>
+
+ <filter>
+ <whitelist>
+ <directory suffix=".php">src</directory>
+ </whitelist>
+ </filter>
+
+ <logging>
+ <log type="coverage-clover" target="build/logs/clover.xml"/>
+ </logging>
+
+</phpunit> \ No newline at end of file
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/src/getallheaders.php b/bin/wiki/vendor/ralouphie/getallheaders/src/getallheaders.php
new file mode 100644
index 00000000..c7285a5b
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/src/getallheaders.php
@@ -0,0 +1,46 @@
+<?php
+
+if (!function_exists('getallheaders')) {
+
+ /**
+ * Get all HTTP header key/values as an associative array for the current request.
+ *
+ * @return string[string] The HTTP header key/value pairs.
+ */
+ function getallheaders()
+ {
+ $headers = array();
+
+ $copy_server = array(
+ 'CONTENT_TYPE' => 'Content-Type',
+ 'CONTENT_LENGTH' => 'Content-Length',
+ 'CONTENT_MD5' => 'Content-Md5',
+ );
+
+ foreach ($_SERVER as $key => $value) {
+ if (substr($key, 0, 5) === 'HTTP_') {
+ $key = substr($key, 5);
+ if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
+ $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
+ $headers[$key] = $value;
+ }
+ } elseif (isset($copy_server[$key])) {
+ $headers[$copy_server[$key]] = $value;
+ }
+ }
+
+ if (!isset($headers['Authorization'])) {
+ if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
+ $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+ } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
+ $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
+ $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
+ } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
+ $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
+ }
+ }
+
+ return $headers;
+ }
+
+}
diff --git a/bin/wiki/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php b/bin/wiki/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php
new file mode 100644
index 00000000..8e3d1790
--- /dev/null
+++ b/bin/wiki/vendor/ralouphie/getallheaders/tests/GetAllHeadersTest.php
@@ -0,0 +1,121 @@
+<?php
+
+class GetAllHeadersTest extends \PHPUnit_Framework_TestCase
+{
+
+ /**
+ * @dataProvider testWorksData
+ */
+ public function testWorks($test_type, $expected, $server)
+ {
+ foreach ($server as $key => $val) {
+ $_SERVER[$key] = $val;
+ }
+ $result = getallheaders();
+ $this->assertEquals($expected, $result, "Error testing $test_type works.");
+ }
+
+ public function testWorksData()
+ {
+ return array(
+ array(
+ 'normal case',
+ array(
+ 'Key-One' => 'foo',
+ 'Key-Two' => 'bar',
+ 'Another-Key-For-Testing' => 'baz'
+ ),
+ array(
+ 'HTTP_KEY_ONE' => 'foo',
+ 'HTTP_KEY_TWO' => 'bar',
+ 'HTTP_ANOTHER_KEY_FOR_TESTING' => 'baz'
+ )
+ ),
+ array(
+ 'Content-Type',
+ array(
+ 'Content-Type' => 'two'
+ ),
+ array(
+ 'HTTP_CONTENT_TYPE' => 'one',
+ 'CONTENT_TYPE' => 'two'
+ )
+ ),
+ array(
+ 'Content-Length',
+ array(
+ 'Content-Length' => '222'
+ ),
+ array(
+ 'CONTENT_LENGTH' => '222',
+ 'HTTP_CONTENT_LENGTH' => '111'
+ )
+ ),
+ array(
+ 'Content-Length (HTTP_CONTENT_LENGTH only)',
+ array(
+ 'Content-Length' => '111'
+ ),
+ array(
+ 'HTTP_CONTENT_LENGTH' => '111'
+ )
+ ),
+ array(
+ 'Content-MD5',
+ array(
+ 'Content-Md5' => 'aef123'
+ ),
+ array(
+ 'CONTENT_MD5' => 'aef123',
+ 'HTTP_CONTENT_MD5' => 'fea321'
+ )
+ ),
+ array(
+ 'Content-MD5 (HTTP_CONTENT_MD5 only)',
+ array(
+ 'Content-Md5' => 'f123'
+ ),
+ array(
+ 'HTTP_CONTENT_MD5' => 'f123'
+ )
+ ),
+ array(
+ 'Authorization (normal)',
+ array(
+ 'Authorization' => 'testing'
+ ),
+ array(
+ 'HTTP_AUTHORIZATION' => 'testing',
+ )
+ ),
+ array(
+ 'Authorization (redirect)',
+ array(
+ 'Authorization' => 'testing redirect'
+ ),
+ array(
+ 'REDIRECT_HTTP_AUTHORIZATION' => 'testing redirect',
+ )
+ ),
+ array(
+ 'Authorization (PHP_AUTH_USER + PHP_AUTH_PW)',
+ array(
+ 'Authorization' => 'Basic ' . base64_encode('foo:bar')
+ ),
+ array(
+ 'PHP_AUTH_USER' => 'foo',
+ 'PHP_AUTH_PW' => 'bar'
+ )
+ ),
+ array(
+ 'Authorization (PHP_AUTH_DIGEST)',
+ array(
+ 'Authorization' => 'example-digest'
+ ),
+ array(
+ 'PHP_AUTH_DIGEST' => 'example-digest'
+ )
+ )
+ );
+ }
+}