Phriction Welcome in gitpull.it, a Phabricator instance! First steps with boz-mw MediaWiki API framework History Version 32 vs 43
Version 32 vs 43
Version 32 vs 43
Edits
Edits
- Edit by valerio.bozzolan, Version 43
- Mar 10 2023 09:43
- Edit by valerio.bozzolan, Version 32
- Mar 10 2023 09:03
Edit Older Version 32... | Edit Older Version 43... |
Content Changes
Content Changes
This is `boz-mw`, //another MediaWiki API handler in PHP// with batteries included! It has a tons of features that will make your head spin!
This is a library to interact with MediaWiki and Wikibase APIs. There are also some [[ first_steps_with_boz-mw/tools/ | boz-mw command line tools ]].
## Features
You may ask what we can offer to you:
* read/write support for Wikidata
* read/write support for Wikimedia Commons' **Structured Data**
* **file upload** support for Wikimedia Commons
* support for some other known wiki(s)
* support for your custom wiki
* lightweight project, well designed using [[ https://en.wikipedia.org/wiki/Object-oriented_programming | OOP ]]
* it does not require Composer, but it's on Composer
* it supports all versions from PHP 5.5 to PHP 8.2
Actually, this framework is useful for:
* developers, to create bots
* command line users for our [[ #Command line tools ]]
## Download
```
git clone https://gitpull.it/source/boz-mw.git
```
## Install
```
apt install php-cli php-curl
```
## Command line tools
See [[ first_steps_with_boz-mw/tools/ | command line tools ]].
### Command line script `replace.php`
The [replace.php](https://gitpull.it/source/boz-mw/browse/master/tools/#replace-script-tt-class-remarkup) allows you to do some sobstitutions in a wiki.
### Command line script `mega-export.php`
The [mega-export.php](https://gitpull.it/source/boz-mw/browse/master/tools/#mega-export-tt-class-remarkup) allows you to export the _full_ page history of whatever page.
## API framework showcase
Here some usage examples.
### Start: select your wiki
First of all you should init a wiki:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load Wikidata
$wikidata = wikidata();
// load Wikidata (this is the same)
// $wikidata = wiki( 'wikidatawiki' );
// load Wikidata (this is the same)
// $wikidata = \wm\Wikidata::instance();
// load Wikipedia in Italian (this is the same)
$itwiki = itwiki();
// load Wikipedia in Italian (this is the same)
// $itwiki = wiki( 'itwiki' );
// load Wikimedia Commons
$commons = commons();
// load Wikimedia Commons (this is the same)
// $commons = wiki( 'commonswiki' );
// load Wikimedia Commons (this is the same)
// $commons = \wm\Commons::instance();
// load Meta-Wiki
$meta = meta();
// load Meta-wiki (this is the same)
// $meta = wiki( 'metawiki' );
// load Meta-wiki this is the same
// $meta = \wm\MetaWiki::instance();
```
### Basic MediaWiki API read query
This is a very basic example to read from a very generic MediaWiki API.
This example is over-simplified, with the assumption that you get just one result. Spoiler: this never happens in real life, but it's just an idea.
To obtain a simple information from the server (this example has no support to "query continuation" for multiple results):
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load it.wiki
$wiki = itwiki();
$response =
$wiki->fetch( [
'action' => 'query',
'prop' => 'info',
'titles' => 'Pagina principale',
] );
// dramatically take the very first page since you requested just one page
$page = array_pop( $response->query->pages );
// show interesting information
print_r( $page->title );
print_r( $page->pageid );
```
Note: See [[ https://meta.wikimedia.org/w/api.php?action=help&modules=query | MediaWiki API action=query documentation ]].
### API query with continuation
To obtain a long result set from the server (with continuation support):
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wiki = itwiki();
$request =
$wiki->createQuery( [
'action' => 'query',
'list' => 'categorymembers',
'cmtitle' => 'Categoria:Software con licenza GNU GPL',
] );
// automatic query continuation
foreach( $request as $response ) {
// loop each category member
$pages = $response->query->categorymembers ?? [];
foreach( $pages as $page ) {
// do something
var_dump( $page->pageid );
var_dump( $page->ns );
var_dump( $page->title );
print_r( $page );
}
}
```
NOTE: See [[ https://meta.wikimedia.org/w/api.php?action=help&modules=query%2Bcategorymembers | MediaWiki API action=query list=categorymembers documentation ]].
### Login and Edit API query
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// insert here your bot username and password from your wiki
// https://meta.wikimedia.org/wiki/Special:BotPasswords
$bot_user = '';
$bot_password = '';
// pick a wiki
$wiki = itwiki();
// login
$wiki->login( $bot_user, $bot_password );
// save a page
// if something bad happen here, it will quit your program (throwing a detailed exception)
$response = $wiki->edit( [
'title' => 'Wikipedia:Pagina delle prove',
'text' => 'My test wikitext with boz-mw (sorry for this test)',
'summary' => 'My test edit summary with boz-mw (sorry for this test)',
] );
// do something here
// a number
var_dump( $response->edit->pageid );
// a number
var_dump( $response->edit->oldrevid );
// a number
var_dump( $response->edit->newrevid );
```
This example works. If you run this example this page will be edited:
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove
Please do not abuse!
NOTE: See [[ https://mediawiki.org/w/api.php?action=help&modules=edit | MediaWiki action=edit documentation ]]. Yes, the `token` parameter is automatically handled for you automagically.
### Quick start bot wizard and serious-business error management
Do you need to configure your bot with its username and password? We have a wizard for that!
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
/*
* Check your configuration
*
* A nice wizard will help you if it does not exist <3
*/
config_wizard( 'config.php' );
/*
* Login in your wiki
*
* It uses your bot username and password from your config.
*/
$wiki = itwiki()->login();
/*
* Set the page you want to edit
*
* This is an not-writable example to try what happens! <3
*/
$page_title = 'Special:Nothing';
/**
* Try to save the page
*
* The "try" block is useful to "catch" errors instead of dying.
*
*/
try {
// try to save a page
$response = $wiki->edit( [
'title' => $page_title,
'text' => 'My test wikitext with boz-mw (sorry for this test)',
'summary' => 'My test edit summary with boz-mw (sorry for this test)',
] );
// if you are here everything was OK
// do something here
// a number
var_dump( $response->edit->pageid );
// a number
var_dump( $response->edit->oldrevid );
// a number
var_dump( $response->edit->newrevid );
/**
* The catch block can be used to detect a specific exception
*
* Some of them:
* MaxLagException
* PageCannotExistException
* EditConflictException
* ProtectedPageException
* ReadApiDeniedException
* ReadOnlyException
* PermissionDeniedException
* PageCannotExistException
*
* and more!
*
* In this case we will detect the 'page cannot exist' exception.
*/
} catch( API\PageCannotExistException $e ) {
echo "Sorry but you are editing a page that cannot exist. \n";
// generic error handler
} catch( Exception $e ) {
// other exceptions
echo "Something unexpected happened: ";
echo $e->getMessage();
echo "\n";
}
// do something in any case
echo "End. \n";
```
This example works. If you run this example this page will be edited:
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove
Please do not abuse!
NOTE: The wizard will just create a configuration file with these constants inside:
```
\mw\API::$DEFAULT_USERNAME = '':
\mw\API::$DEFAULT_PASSWORD = '';
```
### Wikidata SPARQL query
What if you want to list all the [cats from Wikidata](https://query.wikidata.org/#%23Cats%0ASELECT%20%3Fitem%20%3FitemLabel%20%0AWHERE%20%0A%7B%0A%20%20%3Fitem%20wdt%3AP31%20wd%3AQ146.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22%5BAUTO_LANGUAGE%5D%2Cen%22.%20%7D%0A%7D)?
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wikidata = wikidata();
// you should know how to build a SPARQL query
$query = 'SELECT ?item ?itemLabel WHERE {';
$query .= ' ?item wdt:P31 wd:Q146 ';
$query .= ' SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } ';
$query .= '}';
// query Wikidata and decode the response
$rows = $wikidata::querySPARQL( $query );
// for each cat
foreach( $rows as $row ) {
// example: 'http://www.wikidata.org/entity/Q5317221'
$url = $row->item->value;
// example: 'Q5317221'
$id = basename( $url );
// example: 'Dusty the Klepto Kitty'
$itemLabel = $row->itemLabel->value;
echo "Found cat ID: $id. Name: $itemLabel \n";
}
```
### Wikidata read API
Reading a Wikidata entity is really simple.
Under the hood it uses this API entry point:
https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities
But the wrapper is really simplified.
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wikidata = wiki( 'wikidatawiki' );
// query the Wikidata Sandbox
$data = $wikidata->fetchSingleEntity( 'Q4115189', [
'props' => [
'descriptions',
'labels',
'claims',
],
] );
// get descriptions and labels
$italian_description = $data->getDescriptionValue( 'it' );
$italian_label = $data->getLabelValue( 'it' );
// look all the Claims
var_dump( $data->getClaimsGrouped() );
```
### Wikidata edit API
Editing a Wikidata item, honestly, it's stuff for advanced people, but our wrapper simplifies lot of things. Trust me.
Under the hood it uses this API entry point:
https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity
In this example you:
* overwrite a Wikidata label
* overwrite a Wikidata description
* add a Commons Category (← this is called a "Claim" in Wikidata)
To do all these changes, here a full example:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load a dedicated configuration - it will create that if missing
config_wizard( 'config.php' );
$wikidata = wiki( 'wikidatawiki' );
// this object registers your proposed changes
$newdata = $wikidata->createDataModel();
// prepare a new Wikimedia Commons category
$statement = new \wb\StatementCommonsCategory( 'P373', 'Test category name' );
$newdata->addClaim( $statement );
$newdata->setLabelValue( 'en', "New label" );
$newdata->setDescriptionValue( 'en', "New Description" );
$wikidata->login();
// this tries to save all your proposed changes in the Wikidata Sandbox
$newdata->editEntity( [
'id' => 'Q4115189',
] );
```
### Upload API query
Uploading a file requires to respect the [RFC1341](https://tools.ietf.org/html/rfc1341) about an HTTP multipart request.
Well, we made it easy:
```
<?php
require 'boz-mw/autoload.php';
// this can be an URL or a local pathname
$photo_url = 'http://.../libre-image.jpg';
$wiki->upload( [
'comment' => 'upload file about...',
'text' => 'bla bla [[bla]]',
'filename' => 'Libre image.jpg',
\network\ContentDisposition::createFromNameURLType( 'file', $photo_url, 'image/jpg' ),
] );
```
NOTE: See [[ https://www.mediawiki.org/wiki/Special:ApiHelp/upload | MediaWiki API action=upload documentation ]].
Eventually see the [[ https://gitpull.it/source/boz-mw/browse/master/include/network/ContentDisposition.php | ContentDisposition ]] class for some other constructors.
### Where to test
Please use your own wiki to test this framework or at least use the Wikimedia Wikis' Sandboxes!
Some known pages you can destroy:
* https://www.wikidata.org/wiki/Q4115189
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove_di_Wikidata
* https://en.wikipedia.org/wiki/Wikipedia:Wikidata/Wikidata_Sandbox
* etc.
### How to add another Wiki
Thank you for adding another wiki!
To create another wiki, create a file similar to this one:
https://gitpull.it/source/boz-mw/browse/master/include/web/LandscapeforWiki.php
Then, mention it in the list called `MediaWikis::allClasses()`. Example:
https://gitpull.it/source/boz-mw/browse/master/include/web/MediaWikis.php
Feel free to commit your wiki in the core and send upstream! So other can contribute.
### How to contribute? How to pull request?
Thank you for your code contributions! Follow the instructions in the Download and Install sections. Then:
Please start creating a Task here first:
* #boz-mw_mediawiki_api_client
Describe your problem / goal. Then we can help you in sharing your patch with upstream.
After you created the Task, the best method is, run this command from your local repository:
```
arc diff
```
And follow the wizard.
https://we.phorge.it/book/phorge/article/arcanist_quick_start/
If you have no time for all this mess, feel free to just do a pull request on the proprietary GitHub platform:
https://github.com/valerio-bozzolan/boz-mw/
Bonus point: after your pull request, please create an account in gitpull.it:
https://gitpull.it/auth/start/
If you do a pull request in GitHub, it's OK: I will do my best to merge your patch with my weird workflow Phabricator-based, for you. You're welcome!
### Other examples?
Feel free to fork and improve this documentation! Or just look inside the `/include` directory where there is some inline documentation for you!
## Troubleshooting
You can enable debug mode with `bozmw_debug()`. Example:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
bozmw_debug();
// other code
```
## Testing Unit Files
To run the Unit tests, just execute this in the main directory:
```
phpunit --bootstrap=phpunit/load.php --testdox phpunit
```
## Known usages
* [MediaWikiOrphanizerBot](https://github.com/valerio-bozzolan/MediaWikiOrphanizerBot)
* [ItalianWikipediaDeletionBot](https://github.com/valerio-bozzolan/ItalianWikipediaDeletionBot)
* [ItalianWikipediaListAdmins](https://github.com/valerio-bozzolan/ItalianWikipediaListAdmins)
* [Wikimedia Commons volleyball players uploader bot](https://gitpull.it/source/Wikimedia-Valerio-Bozzolan-bot-tasks/browse/master/2019-05-commons-volleyball-players-upload/)
* [2018 MiBACT Wikidata fixed](https://github.com/valerio-bozzolan/Wikimedia-Valerio-Bozzolan-bot-tasks/tree/master/2018-09-mibact-fixer)
* [2018 Wiki loves monuments CH](https://github.com/valerio-bozzolan/Wikimedia-Valerio-Bozzolan-bot-tasks/tree/master/2018-08-wiki-loves-monuments-switzerland)
* [wiki-users-leaflet](https://github.com/valerio-bozzolan/wiki-users-leaflet/) hosted at [WMF Labs](https://tools.wmflabs.org/it-wiki-users-leaflet/)
This is `boz-mw`, //another MediaWiki API handler in PHP// with batteries included! It has a tons of features that will make your head spin!
This is a library to interact with MediaWiki and Wikibase APIs. There are also some [[ first_steps_with_boz-mw/tools/ | boz-mw command line tools ]].
## Features
You may ask what we can offer to you:
* read/write support for Wikidata
* read/write support for Wikimedia Commons' **Structured Data**
* **file upload** support for Wikimedia Commons
* support for some other known wiki(s)
* support for your custom wiki
* lightweight project, well designed using [[ https://en.wikipedia.org/wiki/Object-oriented_programming | OOP ]]
* it does not require Composer, but it's on Composer
* it supports all versions from PHP 5.5 to PHP 8.2
Actually, this framework is useful for:
* developers, to create bots
* command line users for our [[ #Command line tools ]]
## Download
```
git clone https://gitpull.it/source/boz-mw.git
```
## Install
```
apt install php-cli php-curl
```
## Command line tools
See [[ first_steps_with_boz-mw/tools/ | command line tools ]].
### Command line script `replace.php`
The [replace.php](https://gitpull.it/source/boz-mw/browse/master/tools/#replace-script-tt-class-remarkup) allows you to do some sobstitutions in a wiki.
### Command line script `mega-export.php`
The [mega-export.php](https://gitpull.it/source/boz-mw/browse/master/tools/#mega-export-tt-class-remarkup) allows you to export the _full_ page history of whatever page.
## API framework showcase
Here some usage examples.
### Start: select your wiki
First of all you should init a wiki:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load Wikidata
$wikidata = wikidata();
// load Wikidata (this is the same)
// $wikidata = wiki( 'wikidatawiki' );
// load Wikidata (this is the same)
// $wikidata = \wm\Wikidata::instance();
// load Wikipedia in Italian
$itwiki = itwiki();
// load Wikipedia in Italian (this is the same)
// $itwiki = wiki( 'itwiki' );
// load Wikimedia Commons
$commons = commons();
// load Wikimedia Commons (this is the same)
// $commons = wiki( 'commonswiki' );
// load Wikimedia Commons (this is the same)
// $commons = \wm\Commons::instance();
// load Meta-Wiki
$meta = meta();
// load Meta-wiki (this is the same)
// $meta = wiki( 'metawiki' );
// load Meta-wiki this is the same
// $meta = \wm\MetaWiki::instance();
```
NOTE: All these commented variants are valid for you. Try them all and adopt the most lovely one.
NOTE: I love the variant `wiki( NAME )` since it's flexible. But note that some wiki names are intuitive like `'itwiki'` or `'enwiki'`. Some are not intuitive. For example Wikidata and Wikimedia Commons, for historical reasons caused by them, are called `'wikidatawiki'` and `'commonswiki`'. This is not intuitive. I hate it. But hey, it's not my fault. I just try to adopt official names.
### Basic MediaWiki API read query
You probably want a very basic example to get info from a very generic API of a MediaWiki.
Here an over-simplified example, with the assumption that you get just one result:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load the Italian Wikipedia
$wiki = wiki( 'itwiki' );
// search this specific title
$search_title = "Pagina principale";
$response =
$wiki->fetch( [
'action' => 'query',
'prop' => 'info',
'titles' => $search_title,
] );
// we expect just one damn page
$pages = (array) $response->query->pages;
$page = array_pop( $pages );
// show interesting information
var_dump( $page->title );
var_dump( $page->pageid );
```
NOTE: See the [[ https://meta.wikimedia.org/w/api.php?action=help&modules=query | MediaWiki API action=query documentation ]].
This example is over-simplified with bad pratices. In the real life you need to loop multiple results. Also, only few pages are returned for each request, and you need to execute multiple queries to "continue" and get all remaining results. See the next example for that.
### API query with continuation
To obtain a long result set from the server (with continuation support):
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wiki = itwiki();
$request =
$wiki->createQuery( [
'action' => 'query',
'list' => 'categorymembers',
'cmtitle' => 'Categoria:Software con licenza GNU GPL',
] );
// automatic query continuation
foreach( $request as $response ) {
// loop each category member
$pages = $response->query->categorymembers ?? [];
foreach( $pages as $page ) {
// do something
var_dump( $page->pageid );
var_dump( $page->ns );
var_dump( $page->title );
print_r( $page );
}
}
```
NOTE: See the [[ https://meta.wikimedia.org/w/api.php?action=help&modules=query%2Bcategorymembers | MediaWiki API action=query list=categorymembers documentation ]].
NOTE: You can adapt the above example to read 99.99% of MediaWiki APIs. Just change the arguments inside `createQuery()` to reflect the official MediaWiki API documentation page.
NOTE: If you are thinking "Oh my God! This is too difficult!" - trust me, doing an "API continuation" by hand on yourself is really, really tiring. There is a lot of stuff under the hood to make this code so readable and easier for you. After the first request, it is actually automatically expanding it, to ask for the other results from the server, until you get them all. It's magic, it works, and it's easy to be debugged. You are welcome.
### Login and Edit API query
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// insert here your bot username and password from your wiki
// https://meta.wikimedia.org/wiki/Special:BotPasswords
$bot_user = '';
$bot_password = '';
// pick a wiki
$wiki = itwiki();
// login
$wiki->login( $bot_user, $bot_password );
// save a page
// if something bad happen here, it will quit your program (throwing a detailed exception)
$response = $wiki->edit( [
'title' => 'Wikipedia:Pagina delle prove',
'text' => 'My test wikitext with boz-mw (sorry for this test)',
'summary' => 'My test edit summary with boz-mw (sorry for this test)',
] );
// do something here
// a number
var_dump( $response->edit->pageid );
// a number
var_dump( $response->edit->oldrevid );
// a number
var_dump( $response->edit->newrevid );
```
This example works. If you run this example this page will be edited:
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove
Please do not abuse!
NOTE: See [[ https://mediawiki.org/w/api.php?action=help&modules=edit | MediaWiki action=edit documentation ]]. Yes, the `token` parameter is automatically handled for you automagically.
### Quick start bot wizard and serious-business error management
Do you need to configure your bot with its username and password? We have a wizard for that!
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
/*
* Check your configuration
*
* A nice wizard will help you if it does not exist <3
*/
config_wizard( 'config.php' );
/*
* Login in your wiki
*
* It uses your bot username and password from your config.
*/
$wiki = itwiki()->login();
/*
* Set the page you want to edit
*
* This is an not-writable example to try what happens! <3
*/
$page_title = 'Special:Nothing';
/**
* Try to save the page
*
* The "try" block is useful to "catch" errors instead of dying.
*
*/
try {
// try to save a page
$response = $wiki->edit( [
'title' => $page_title,
'text' => 'My test wikitext with boz-mw (sorry for this test)',
'summary' => 'My test edit summary with boz-mw (sorry for this test)',
] );
// if you are here everything was OK
// do something here
// a number
var_dump( $response->edit->pageid );
// a number
var_dump( $response->edit->oldrevid );
// a number
var_dump( $response->edit->newrevid );
/**
* The catch block can be used to detect a specific exception
*
* Some of them:
* MaxLagException
* PageCannotExistException
* EditConflictException
* ProtectedPageException
* ReadApiDeniedException
* ReadOnlyException
* PermissionDeniedException
* PageCannotExistException
*
* and more!
*
* In this case we will detect the 'page cannot exist' exception.
*/
} catch( API\PageCannotExistException $e ) {
echo "Sorry but you are editing a page that cannot exist. \n";
// generic error handler
} catch( Exception $e ) {
// other exceptions
echo "Something unexpected happened: ";
echo $e->getMessage();
echo "\n";
}
// do something in any case
echo "End. \n";
```
This example works. If you run this example this page will be edited:
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove
Please do not abuse!
NOTE: The wizard will just create a configuration file with these constants inside:
```
\mw\API::$DEFAULT_USERNAME = '':
\mw\API::$DEFAULT_PASSWORD = '';
```
### Wikidata SPARQL query
What if you want to list all the [cats from Wikidata](https://query.wikidata.org/#%23Cats%0ASELECT%20%3Fitem%20%3FitemLabel%20%0AWHERE%20%0A%7B%0A%20%20%3Fitem%20wdt%3AP31%20wd%3AQ146.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22%5BAUTO_LANGUAGE%5D%2Cen%22.%20%7D%0A%7D)?
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wikidata = wikidata();
// you should know how to build a SPARQL query
$query = 'SELECT ?item ?itemLabel WHERE {';
$query .= ' ?item wdt:P31 wd:Q146 ';
$query .= ' SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } ';
$query .= '}';
// query Wikidata and decode the response
$rows = $wikidata::querySPARQL( $query );
// for each cat
foreach( $rows as $row ) {
// example: 'http://www.wikidata.org/entity/Q5317221'
$url = $row->item->value;
// example: 'Q5317221'
$id = basename( $url );
// example: 'Dusty the Klepto Kitty'
$itemLabel = $row->itemLabel->value;
echo "Found cat ID: $id. Name: $itemLabel \n";
}
```
### Wikidata read API
Reading a Wikidata entity is really simple.
Under the hood it uses this API entry point:
https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities
But the wrapper is really simplified.
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wikidata = wiki( 'wikidatawiki' );
// query the Wikidata Sandbox
$data = $wikidata->fetchSingleEntity( 'Q4115189', [
'props' => [
'descriptions',
'labels',
'claims',
],
] );
// get descriptions and labels
$italian_description = $data->getDescriptionValue( 'it' );
$italian_label = $data->getLabelValue( 'it' );
// look all the Claims
var_dump( $data->getClaimsGrouped() );
```
### Wikidata edit API
Editing a Wikidata item, honestly, it's stuff for advanced people, but our wrapper simplifies lot of things. Trust me.
Under the hood it uses this API entry point:
https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity
In this example you:
* overwrite a Wikidata label
* overwrite a Wikidata description
* add a Commons Category (← this is called a "Claim" in Wikidata)
To do all these changes, here a full example:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load a dedicated configuration - it will create that if missing
config_wizard( 'config.php' );
$wikidata = wiki( 'wikidatawiki' );
// this object registers your proposed changes
$newdata = $wikidata->createDataModel();
// prepare a new Wikimedia Commons category
$statement = new \wb\StatementCommonsCategory( 'P373', 'Test category name' );
$newdata->addClaim( $statement );
$newdata->setLabelValue( 'en', "New label" );
$newdata->setDescriptionValue( 'en', "New Description" );
$wikidata->login();
// this tries to save all your proposed changes in the Wikidata Sandbox
$newdata->editEntity( [
'id' => 'Q4115189',
] );
```
### Upload API query
Uploading a file requires to respect the [RFC1341](https://tools.ietf.org/html/rfc1341) about an HTTP multipart request.
Well, we made it easy:
```
<?php
require 'boz-mw/autoload.php';
// this can be an URL or a local pathname
$photo_url = 'http://.../libre-image.jpg';
$wiki->upload( [
'comment' => 'upload file about...',
'text' => 'bla bla [[bla]]',
'filename' => 'Libre image.jpg',
\network\ContentDisposition::createFromNameURLType( 'file', $photo_url, 'image/jpg' ),
] );
```
NOTE: See [[ https://www.mediawiki.org/wiki/Special:ApiHelp/upload | MediaWiki API action=upload documentation ]].
Eventually see the [[ https://gitpull.it/source/boz-mw/browse/master/include/network/ContentDisposition.php | ContentDisposition ]] class for some other constructors.
### Where to test
Please use your own wiki to test this framework or at least use the Wikimedia Wikis' Sandboxes!
Some known pages you can destroy:
* https://www.wikidata.org/wiki/Q4115189
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove_di_Wikidata
* https://en.wikipedia.org/wiki/Wikipedia:Wikidata/Wikidata_Sandbox
* etc.
### How to add another Wiki
Thank you for adding another wiki!
To create another wiki, create a file similar to this one:
https://gitpull.it/source/boz-mw/browse/master/include/web/LandscapeforWiki.php
Then, mention it in the list called `MediaWikis::allClasses()`. Example:
https://gitpull.it/source/boz-mw/browse/master/include/web/MediaWikis.php
Feel free to commit your wiki in the core and send upstream! So other can contribute.
### How to contribute? How to pull request?
Thank you for your code contributions! Follow the instructions in the Download and Install sections. Then:
Please start creating a Task here first:
* #boz-mw_mediawiki_api_client
Describe your problem / goal. Then we can help you in sharing your patch with upstream.
After you created the Task, the best method is, run this command from your local repository:
```
arc diff
```
And follow the wizard.
https://we.phorge.it/book/phorge/article/arcanist_quick_start/
If you have no time for all this mess, feel free to just do a pull request on the proprietary GitHub platform:
https://github.com/valerio-bozzolan/boz-mw/
Bonus point: after your pull request, please create an account in gitpull.it:
https://gitpull.it/auth/start/
If you do a pull request in GitHub, it's OK: I will do my best to merge your patch with my weird workflow Phabricator-based, for you. You're welcome!
### Other examples?
Feel free to fork and improve this documentation! Or just look inside the `/include` directory where there is some inline documentation for you!
## Troubleshooting
You can enable debug mode with `bozmw_debug()`. Example:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
bozmw_debug();
// other code
```
## Testing Unit Files
To run the Unit tests, just execute this in the main directory:
```
phpunit --bootstrap=phpunit/load.php --testdox phpunit
```
## Known usages
* [MediaWikiOrphanizerBot](https://github.com/valerio-bozzolan/MediaWikiOrphanizerBot)
* [ItalianWikipediaDeletionBot](https://github.com/valerio-bozzolan/ItalianWikipediaDeletionBot)
* [ItalianWikipediaListAdmins](https://github.com/valerio-bozzolan/ItalianWikipediaListAdmins)
* [Wikimedia Commons volleyball players uploader bot](https://gitpull.it/source/Wikimedia-Valerio-Bozzolan-bot-tasks/browse/master/2019-05-commons-volleyball-players-upload/)
* [2018 MiBACT Wikidata fixed](https://github.com/valerio-bozzolan/Wikimedia-Valerio-Bozzolan-bot-tasks/tree/master/2018-09-mibact-fixer)
* [2018 Wiki loves monuments CH](https://github.com/valerio-bozzolan/Wikimedia-Valerio-Bozzolan-bot-tasks/tree/master/2018-08-wiki-loves-monuments-switzerland)
* [wiki-users-leaflet](https://github.com/valerio-bozzolan/wiki-users-leaflet/) hosted at [WMF Labs](https://tools.wmflabs.org/it-wiki-users-leaflet/)
This is `boz-mw`, //another MediaWiki API handler in PHP// with batteries included! It has a tons of features that will make your head spin!
This is a library to interact with MediaWiki and Wikibase APIs. There are also some [[ first_steps_with_boz-mw/tools/ | boz-mw command line tools ]].
## Features
You may ask what we can offer to you:
* read/write support for Wikidata
* read/write support for Wikimedia Commons' **Structured Data**
* **file upload** support for Wikimedia Commons
* support for some other known wiki(s)
* support for your custom wiki
* lightweight project, well designed using [[ https://en.wikipedia.org/wiki/Object-oriented_programming | OOP ]]
* it does not require Composer, but it's on Composer
* it supports all versions from PHP 5.5 to PHP 8.2
Actually, this framework is useful for:
* developers, to create bots
* command line users for our [[ #Command line tools ]]
## Download
```
git clone https://gitpull.it/source/boz-mw.git
```
## Install
```
apt install php-cli php-curl
```
## Command line tools
See [[ first_steps_with_boz-mw/tools/ | command line tools ]].
### Command line script `replace.php`
The [replace.php](https://gitpull.it/source/boz-mw/browse/master/tools/#replace-script-tt-class-remarkup) allows you to do some sobstitutions in a wiki.
### Command line script `mega-export.php`
The [mega-export.php](https://gitpull.it/source/boz-mw/browse/master/tools/#mega-export-tt-class-remarkup) allows you to export the _full_ page history of whatever page.
## API framework showcase
Here some usage examples.
### Start: select your wiki
First of all you should init a wiki:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load Wikidata
$wikidata = wikidata();
// load Wikidata (this is the same)
// $wikidata = wiki( 'wikidatawiki' );
// load Wikidata (this is the same)
// $wikidata = \wm\Wikidata::instance();
// load Wikipedia in Italian (this is the same)
$itwiki = itwiki();
// load Wikipedia in Italian (this is the same)
// $itwiki = wiki( 'itwiki' );
// load Wikimedia Commons
$commons = commons();
// load Wikimedia Commons (this is the same)
// $commons = wiki( 'commonswiki' );
// load Wikimedia Commons (this is the same)
// $commons = \wm\Commons::instance();
// load Meta-Wiki
$meta = meta();
// load Meta-wiki (this is the same)
// $meta = wiki( 'metawiki' );
// load Meta-wiki this is the same
// $meta = \wm\MetaWiki::instance();
```
### Basic MediaWiki API read queryNOTE: All these commented variants are valid for you. Try them all and adopt the most lovely one.
This is a very basic example to read from a very generic MediaWiki API.NOTE: I love the variant `wiki( NAME )` since it's flexible. But note that some wiki names are intuitive like `'itwiki'` or `'enwiki'`. Some are not intuitive. For example Wikidata and Wikimedia Commons, for historical reasons caused by them, are called `'wikidatawiki'` and `'commonswiki`'. This is not intuitive. I hate it. But hey, it's not my fault. I just try to adopt official names.
### Basic MediaWiki API read query
This example is over-simplified, with the assumption that you get just one result. Spoiler: this never happens in real life, but it's just an ideaYou probably want a very basic example to get info from a very generic API of a MediaWiki.
To obtain a Here an over-simple information from the server (this example has no support to "query continuation" for multiplified example, with the assumption that you get just one results)::
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load it.wikithe Italian Wikipedia
$wiki = wiki( 'itwiki();' );
// search this specific title
$search_title = "Pagina principale";
$response =
$wiki->fetch( [
'action' => 'query',
'prop' => 'info',
'titles' => 'Pagina principale'$search_title,
] );
// dramatically take the very first page since you requested just one// we expect just one damn page
$page = s = (array_pop() $response->query->pages;
$page = array_pop( $pages );
// show interesting information
print_rvar_dump( $page->title );
print_rvar_dump( $page->pageid );
```
Note: SeNOTE: See the [[ https://meta.wikimedia.org/w/api.php?action=help&modules=query | MediaWiki API action=query documentation ]].
This example is over-simplified with bad pratices. In the real life you need to loop multiple results. Also, only few pages are returned for each request, and you need to execute multiple queries to "continue" and get all remaining results. See the next example for that.
### API query with continuation
To obtain a long result set from the server (with continuation support):
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wiki = itwiki();
$request =
$wiki->createQuery( [
'action' => 'query',
'list' => 'categorymembers',
'cmtitle' => 'Categoria:Software con licenza GNU GPL',
] );
// automatic query continuation
foreach( $request as $response ) {
// loop each category member
$pages = $response->query->categorymembers ?? [];
foreach( $pages as $page ) {
// do something
var_dump( $page->pageid );
var_dump( $page->ns );
var_dump( $page->title );
print_r( $page );
}
}
```
NOTE: See the [[ https://meta.wikimedia.org/w/api.php?action=help&modules=query%2Bcategorymembers | MediaWiki API action=query list=categorymembers documentation ]].
NOTE: You can adapt the above example to read 99.99% of MediaWiki APIs. Just change the arguments inside `createQuery()` to reflect the official MediaWiki API documentation page.
NOTE: If you are thinking "Oh my God! This is too difficult!" - trust me, doing an "API continuation" by hand on yourself is really, really tiring. There is a lot of stuff under the hood to make this code so readable and easier for you. After the first request, it is actually automatically expanding it, to ask for the other results from the server, until you get them all. It's magic, it works, and it's easy to be debugged. You are welcome.
### Login and Edit API query
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// insert here your bot username and password from your wiki
// https://meta.wikimedia.org/wiki/Special:BotPasswords
$bot_user = '';
$bot_password = '';
// pick a wiki
$wiki = itwiki();
// login
$wiki->login( $bot_user, $bot_password );
// save a page
// if something bad happen here, it will quit your program (throwing a detailed exception)
$response = $wiki->edit( [
'title' => 'Wikipedia:Pagina delle prove',
'text' => 'My test wikitext with boz-mw (sorry for this test)',
'summary' => 'My test edit summary with boz-mw (sorry for this test)',
] );
// do something here
// a number
var_dump( $response->edit->pageid );
// a number
var_dump( $response->edit->oldrevid );
// a number
var_dump( $response->edit->newrevid );
```
This example works. If you run this example this page will be edited:
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove
Please do not abuse!
NOTE: See [[ https://mediawiki.org/w/api.php?action=help&modules=edit | MediaWiki action=edit documentation ]]. Yes, the `token` parameter is automatically handled for you automagically.
### Quick start bot wizard and serious-business error management
Do you need to configure your bot with its username and password? We have a wizard for that!
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
/*
* Check your configuration
*
* A nice wizard will help you if it does not exist <3
*/
config_wizard( 'config.php' );
/*
* Login in your wiki
*
* It uses your bot username and password from your config.
*/
$wiki = itwiki()->login();
/*
* Set the page you want to edit
*
* This is an not-writable example to try what happens! <3
*/
$page_title = 'Special:Nothing';
/**
* Try to save the page
*
* The "try" block is useful to "catch" errors instead of dying.
*
*/
try {
// try to save a page
$response = $wiki->edit( [
'title' => $page_title,
'text' => 'My test wikitext with boz-mw (sorry for this test)',
'summary' => 'My test edit summary with boz-mw (sorry for this test)',
] );
// if you are here everything was OK
// do something here
// a number
var_dump( $response->edit->pageid );
// a number
var_dump( $response->edit->oldrevid );
// a number
var_dump( $response->edit->newrevid );
/**
* The catch block can be used to detect a specific exception
*
* Some of them:
* MaxLagException
* PageCannotExistException
* EditConflictException
* ProtectedPageException
* ReadApiDeniedException
* ReadOnlyException
* PermissionDeniedException
* PageCannotExistException
*
* and more!
*
* In this case we will detect the 'page cannot exist' exception.
*/
} catch( API\PageCannotExistException $e ) {
echo "Sorry but you are editing a page that cannot exist. \n";
// generic error handler
} catch( Exception $e ) {
// other exceptions
echo "Something unexpected happened: ";
echo $e->getMessage();
echo "\n";
}
// do something in any case
echo "End. \n";
```
This example works. If you run this example this page will be edited:
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove
Please do not abuse!
NOTE: The wizard will just create a configuration file with these constants inside:
```
\mw\API::$DEFAULT_USERNAME = '':
\mw\API::$DEFAULT_PASSWORD = '';
```
### Wikidata SPARQL query
What if you want to list all the [cats from Wikidata](https://query.wikidata.org/#%23Cats%0ASELECT%20%3Fitem%20%3FitemLabel%20%0AWHERE%20%0A%7B%0A%20%20%3Fitem%20wdt%3AP31%20wd%3AQ146.%0A%20%20SERVICE%20wikibase%3Alabel%20%7B%20bd%3AserviceParam%20wikibase%3Alanguage%20%22%5BAUTO_LANGUAGE%5D%2Cen%22.%20%7D%0A%7D)?
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wikidata = wikidata();
// you should know how to build a SPARQL query
$query = 'SELECT ?item ?itemLabel WHERE {';
$query .= ' ?item wdt:P31 wd:Q146 ';
$query .= ' SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } ';
$query .= '}';
// query Wikidata and decode the response
$rows = $wikidata::querySPARQL( $query );
// for each cat
foreach( $rows as $row ) {
// example: 'http://www.wikidata.org/entity/Q5317221'
$url = $row->item->value;
// example: 'Q5317221'
$id = basename( $url );
// example: 'Dusty the Klepto Kitty'
$itemLabel = $row->itemLabel->value;
echo "Found cat ID: $id. Name: $itemLabel \n";
}
```
### Wikidata read API
Reading a Wikidata entity is really simple.
Under the hood it uses this API entry point:
https://www.wikidata.org/w/api.php?action=help&modules=wbgetentities
But the wrapper is really simplified.
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
$wikidata = wiki( 'wikidatawiki' );
// query the Wikidata Sandbox
$data = $wikidata->fetchSingleEntity( 'Q4115189', [
'props' => [
'descriptions',
'labels',
'claims',
],
] );
// get descriptions and labels
$italian_description = $data->getDescriptionValue( 'it' );
$italian_label = $data->getLabelValue( 'it' );
// look all the Claims
var_dump( $data->getClaimsGrouped() );
```
### Wikidata edit API
Editing a Wikidata item, honestly, it's stuff for advanced people, but our wrapper simplifies lot of things. Trust me.
Under the hood it uses this API entry point:
https://www.wikidata.org/w/api.php?action=help&modules=wbeditentity
In this example you:
* overwrite a Wikidata label
* overwrite a Wikidata description
* add a Commons Category (← this is called a "Claim" in Wikidata)
To do all these changes, here a full example:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
// load a dedicated configuration - it will create that if missing
config_wizard( 'config.php' );
$wikidata = wiki( 'wikidatawiki' );
// this object registers your proposed changes
$newdata = $wikidata->createDataModel();
// prepare a new Wikimedia Commons category
$statement = new \wb\StatementCommonsCategory( 'P373', 'Test category name' );
$newdata->addClaim( $statement );
$newdata->setLabelValue( 'en', "New label" );
$newdata->setDescriptionValue( 'en', "New Description" );
$wikidata->login();
// this tries to save all your proposed changes in the Wikidata Sandbox
$newdata->editEntity( [
'id' => 'Q4115189',
] );
```
### Upload API query
Uploading a file requires to respect the [RFC1341](https://tools.ietf.org/html/rfc1341) about an HTTP multipart request.
Well, we made it easy:
```
<?php
require 'boz-mw/autoload.php';
// this can be an URL or a local pathname
$photo_url = 'http://.../libre-image.jpg';
$wiki->upload( [
'comment' => 'upload file about...',
'text' => 'bla bla [[bla]]',
'filename' => 'Libre image.jpg',
\network\ContentDisposition::createFromNameURLType( 'file', $photo_url, 'image/jpg' ),
] );
```
NOTE: See [[ https://www.mediawiki.org/wiki/Special:ApiHelp/upload | MediaWiki API action=upload documentation ]].
Eventually see the [[ https://gitpull.it/source/boz-mw/browse/master/include/network/ContentDisposition.php | ContentDisposition ]] class for some other constructors.
### Where to test
Please use your own wiki to test this framework or at least use the Wikimedia Wikis' Sandboxes!
Some known pages you can destroy:
* https://www.wikidata.org/wiki/Q4115189
* https://it.wikipedia.org/wiki/Wikipedia:Pagina_delle_prove_di_Wikidata
* https://en.wikipedia.org/wiki/Wikipedia:Wikidata/Wikidata_Sandbox
* etc.
### How to add another Wiki
Thank you for adding another wiki!
To create another wiki, create a file similar to this one:
https://gitpull.it/source/boz-mw/browse/master/include/web/LandscapeforWiki.php
Then, mention it in the list called `MediaWikis::allClasses()`. Example:
https://gitpull.it/source/boz-mw/browse/master/include/web/MediaWikis.php
Feel free to commit your wiki in the core and send upstream! So other can contribute.
### How to contribute? How to pull request?
Thank you for your code contributions! Follow the instructions in the Download and Install sections. Then:
Please start creating a Task here first:
* #boz-mw_mediawiki_api_client
Describe your problem / goal. Then we can help you in sharing your patch with upstream.
After you created the Task, the best method is, run this command from your local repository:
```
arc diff
```
And follow the wizard.
https://we.phorge.it/book/phorge/article/arcanist_quick_start/
If you have no time for all this mess, feel free to just do a pull request on the proprietary GitHub platform:
https://github.com/valerio-bozzolan/boz-mw/
Bonus point: after your pull request, please create an account in gitpull.it:
https://gitpull.it/auth/start/
If you do a pull request in GitHub, it's OK: I will do my best to merge your patch with my weird workflow Phabricator-based, for you. You're welcome!
### Other examples?
Feel free to fork and improve this documentation! Or just look inside the `/include` directory where there is some inline documentation for you!
## Troubleshooting
You can enable debug mode with `bozmw_debug()`. Example:
```
<?php
require 'boz-mw/autoload-with-laser-cannon.php';
bozmw_debug();
// other code
```
## Testing Unit Files
To run the Unit tests, just execute this in the main directory:
```
phpunit --bootstrap=phpunit/load.php --testdox phpunit
```
## Known usages
* [MediaWikiOrphanizerBot](https://github.com/valerio-bozzolan/MediaWikiOrphanizerBot)
* [ItalianWikipediaDeletionBot](https://github.com/valerio-bozzolan/ItalianWikipediaDeletionBot)
* [ItalianWikipediaListAdmins](https://github.com/valerio-bozzolan/ItalianWikipediaListAdmins)
* [Wikimedia Commons volleyball players uploader bot](https://gitpull.it/source/Wikimedia-Valerio-Bozzolan-bot-tasks/browse/master/2019-05-commons-volleyball-players-upload/)
* [2018 MiBACT Wikidata fixed](https://github.com/valerio-bozzolan/Wikimedia-Valerio-Bozzolan-bot-tasks/tree/master/2018-09-mibact-fixer)
* [2018 Wiki loves monuments CH](https://github.com/valerio-bozzolan/Wikimedia-Valerio-Bozzolan-bot-tasks/tree/master/2018-08-wiki-loves-monuments-switzerland)
* [wiki-users-leaflet](https://github.com/valerio-bozzolan/wiki-users-leaflet/) hosted at [WMF Labs](https://tools.wmflabs.org/it-wiki-users-leaflet/)
Public contents are in Creative Commons Attribution-ShareAlike 4.0 (CC-BY-SA) or GNU Free Documentation License (at your option) unless otherwise noted. · Contact / Register