{"id":17215235,"url":"https://github.com/doctormckay/php-mypdoms","last_synced_at":"2025-04-13T23:16:37.877Z","repository":{"id":56958172,"uuid":"121351506","full_name":"DoctorMcKay/php-mypdoms","owner":"DoctorMcKay","description":"MyPDOMS: A PDO extension for simple MySQL master/slave switching","archived":false,"fork":false,"pushed_at":"2018-11-18T06:30:48.000Z","size":15,"stargazers_count":2,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-13T23:16:32.713Z","etag":null,"topics":["mysql","pdo","php","replication"],"latest_commit_sha":null,"homepage":"https://www.packagist.org/packages/corn/mypdoms","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DoctorMcKay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-02-13T07:22:50.000Z","updated_at":"2019-05-15T13:15:13.000Z","dependencies_parsed_at":"2022-08-21T08:50:56.110Z","dependency_job_id":null,"html_url":"https://github.com/DoctorMcKay/php-mypdoms","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoctorMcKay%2Fphp-mypdoms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoctorMcKay%2Fphp-mypdoms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoctorMcKay%2Fphp-mypdoms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DoctorMcKay%2Fphp-mypdoms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DoctorMcKay","download_url":"https://codeload.github.com/DoctorMcKay/php-mypdoms/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248794569,"owners_count":21162615,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mysql","pdo","php","replication"],"created_at":"2024-10-15T03:23:44.593Z","updated_at":"2025-04-13T23:16:37.833Z","avatar_url":"https://github.com/DoctorMcKay.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MyPDOMS\n\nMyPDOMS is intended to be a drop-in replacement for most common tasks performed with\n[mysqlnd_ms](http://php.net/manual/en/book.mysqlnd-ms.php).\n\nRequires PHP 5.6 or above.\n\n**While this is based on PDO, it only supports MySQL.**\n\n## Table of Contents\n\n- [Configuring](#configuring)\n- [Establishing a Connection](#establishing-a-connection)\n- [Differences from PDO](#differences-from-pdo)\n- [Query Routing](#query-routing)\n- [SQL Hints](#sql-hints)\n- [Slave Selection](#slave-selection)\n- [Prepared Statements](#prepared-statements)\n\n# Configuring\n\nBefore you instantiate an instance of `MyPDOMS`, you need to configure it. Configuration is accomplished using the static\n`setConfig` method, which expects a single parameter of type array. The structure of the expected associative array is:\n\n- `[config name]` - The name of a configuration. Configurations are collections of servers.\n\t- `master` - Database configuration for your master server\n\t\t- `host` - The host where this database is running (required)\n\t\t- `port` - The port on which this database is running (optional; defaults to 3306)\n\t\t- `username` - Database username (optional; defers to constructor argument if missing)\n\t\t- `password` - Database password (optional; defers to constructor argument if missing)\n\t- `slaves` - Contains your slave server database configurations\n\t\t- `[slave name]` - Database configuration for a slave server (can be anything except `master`)\n\t\t\t- `host` - The host where this database is running (required)\n\t\t\t- `port` - The port on which this database is running (optional; defaults to 3306)\n\t\t\t- `username` - Database username (optional; defers to constructor argument if missing)\n\t\t\t- `password` - Database password (optional; defers to constructor argument if missing)\n\nSo, for example, you might want to do this:\n\n```php\n\u003c?php\nuse Corn\\MyPDOMS\\MyPDOMS;\n\nMyPDOMS::setConfig([\n\t'my_site_1' =\u003e [\n\t\t'master' =\u003e [\n\t\t\t'host' =\u003e '127.0.0.1',\n\t\t\t'port' =\u003e 3306,\n\t\t\t'username' =\u003e 'my_site_user',\n\t\t\t'password' =\u003e 'apples'\n\t\t],\n\t\t'slaves' =\u003e [\n\t\t\t'slave_1' =\u003e [\n\t\t\t\t'host' =\u003e '10.0.1.1',\n\t\t\t\t'username' =\u003e 'slave_user',\n\t\t\t\t'password' =\u003e 'readonly'\n\t\t\t],\n\t\t\t'slave_2' =\u003e [\n\t\t\t\t'host' =\u003e '10.0.1.2',\n\t\t\t\t'username' =\u003e 'slave_user',\n\t\t\t\t'password' =\u003e 'readonly'\n\t\t\t]\n\t\t]\n\t],\n\t'my_site_2' =\u003e [\n\t\t'master' =\u003e [\n\t\t\t'host' =\u003e 'localhost',\n\t\t\t'username' =\u003e 'my_site_2_user',\n\t\t\t'password' =\u003e 'oranges'\n\t\t]\n\t]\n]);\n```\n\nYou don't need to supply any slave configuration. If you don't configure any slaves, then all queries will go to the\nmaster.\n\n# Establishing a Connection\n\nSlave connections are lazily-established, but a master connection is established when you construct a new `MyPDOMS`\ninstance. The constructor is identical to the standard [PDO constructor](http://php.net/manual/en/pdo.construct.php)\nbut with these caveats:\n\n- The `host` in the DSN should be the name of one of your configurations (in the above example, `my_site_1` or `my_site_2`)\n- The `port` in the DSN is ignored if supplied\n- If you supply database credentials (`$username` and `$passwd`) in both the constructor and in your config (`setConfig`), the constructor wins\n\t- It's recommended you supply your credentials in `setConfig`, to prevent any possible credential leakage e.g. in stack traces\n- Any connection `$options` you supply will be used for establishing connections to the master **and** to all slave connections\n\nHere's an example:\n\n```php\n\u003c?php\nuse Corn\\MyPDOMS\\MyPDOMS;\n\n$db = new MyPDOMS('mysql:host=my_site_1;dbname=my_database;charset=utf8mb4', null, null, [MyPDOMS::ATTR_TIMEOUT =\u003e 5]);\n```\n\n# Differences from PDO\n\n`MyPDOMS` is a subclass of [`PDO`](http://php.net/manual/en/class.pdo.php) so PDO's documentation also applies to `MyPDOMS`\nwith these core differences:\n\n- The differences noted above in the [Establishing a Connection](#establishing-a-connection) section\n- The `lastUsedHost` property contains the name of the last host that was sent a query (e.g. `master` or `slave_1`)\n- These methods will always be sent to the master connection:\n\t- [`beginTransaction`](http://php.net/manual/en/pdo.begintransaction.php)\n\t- [`commit`](http://php.net/manual/en/pdo.commit.php)\n\t- [`rollBack`](http://php.net/manual/en/pdo.rollback.php)\n\t- [`inTransaction`](http://php.net/manual/en/pdo.intransaction.php)\n\t- [`lastInsertId`](http://php.net/manual/en/pdo.lastinsertid.php)\n\t- [`quote`](http://php.net/manual/en/pdo.quote.php)\n\t\t- Although `quote` does not result in any network I/O, it's always called on the master connection\n- These methods will be sent to the connection named by `lastUsedHost`:\n\t- [`errorCode`](http://php.net/manual/en/pdo.errorcode.php)\n\t- [`errorInfo`](http://php.net/manual/en/pdo.errorinfo.php)\n- [`getAvailableDrivers`](http://php.net/manual/en/pdo.getavailabledrivers.php) will always return `['mysql']`\n- Calling [`setAttribute`](http://php.net/manual/en/pdo.setattribute.php) will result in this sequence of events:\n\t- The attribute and value you passed in will be stored internally in the `MyPDOMS` object\n\t- The attribute will be set on the master connection\n\t- The attribute will be set on any established slave connections\n\t- When a new slave connection is established, all previously-set attributes will be immediately set on it\n\t- Returns `true` iff all connections returned `true` when `setAttribute` was called on them\n- Calling [`getAttribute`](http://php.net/manual/en/pdo.getattribute.php) will return the value from the internal cache, not from a `PDO` connection object\n- [`prepare`](http://php.net/manual/en/pdo.prepare.php), [`query`](http://php.net/manual/en/pdo.query.php), and [`exec`](http://php.net/manual/en/pdo.exec.php) will route to a connection based on the criteria noted in [Query Routing](#query-routing)\n- You can set the property `alwaysUseMaster` to true to always use the master connection, despite wherever the query would ordinarily be routed\n\n# Query Routing\n\nQueries will be routed to either the master or to a slave depending on this sequence of checks:\n\n1. Leading comments in the SQL will be checked for [SQL Hints](#sql-hints)\n\t1. If `HINT_MASTER` is found then the query will be sent to the master\n\t2. If `HINT_SLAVE` is found then the query will be sent to a slave\n\t3. If `HINT_LAST_USED` is found then the query will be sent to the last used connection\n2. If the first SQL-word is one of `INSERT`, `UPDATE`, `DELETE`, `REPLACE`, `LOAD`, `ALTER`, `CREATE`, `DROP`, `RENAME`, or `TRUNCATE` then the query will be sent to the master\n3. If the first SQL-word is `SELECT` and the query ends in `FOR UPDATE` then the query will be sent to the master\n4. If none of the above match, then the query will be sent to a slave\n\nNote: The routing logic does not check to see if a transaction is open, because all queries that can result in updates or locks are already automatically routed to the master.\n\n# SQL Hints\n\nSQL hints can be used to override the default query routing logic.\nThese SQL hints are available, and should be prepended to queries in a comment:\n\n- `MyPDOMS::HINT_MASTER` - Send this query to the master\n- `MyPDOMS::HINT_SLAVE` - Send this query to a slave\n- `MyPDOMS::HINT_LAST_USED` - Send this query to the server last used\n\t- This may be the master, if the master was last used\n\t- If the last used server was a slave, then the query will be sent to that slave\n\t\nExample:\n\n```php\n\u003c?php\nuse Corn\\MyPDOMS\\MyPDOMS;\n\n$db = new MyPDOMS($dsn);\n$db-\u003equery(\"/*\" . MyPDOMS::HINT_MASTER . \"*/SELECT 1\"); // will be sent to the master even though it's a SELECT\n```\n\nIf you want to replace mysqlnd_ms and not go back and update all your code, you can use this snippet:\n\n```php\n\u003c?php\nuse Corn\\MyPDOMS\\MyPDOMS;\n\nif (!defined('MYSQLND_MS_MASTER_SWITCH')) {\n\tdefine('MYSQLND_MS_MASTER_SWITCH', MyPDOMS::HINT_MASTER);\n\tdefine('MYSQLND_MS_SLAVE_SWITCH', MyPDOMS::HINT_SLAVE);\n\tdefine('MYSQLND_MS_LAST_USED_SWITCH', MyPDOMS::HINT_LAST_USED);\n}\n```\n\n# Slave Selection\n\nWhen a query is due to be routed to a slave, a slave is selected **per-query**. That is, slaves are not selected per-request\nbut are selected every time a query is executed. Presently, the only supported selection mechanism is unweighted random,\nin which every query will be sent to a random slave, with all slaves having an equal probability of being chosen.\n\nExample:\n\n```php\n\u003c?php\nuse Corn\\MyPDOMS\\MyPDOMS;\n\n// Assume configured slaves are slave_{1-5}\n\n$db = new MyPDOMS($dsn);\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_4\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_3\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_4\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_2\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_2\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_2\n```\n\nDifferent selection algorithms are expected to be added in later releases, but if you wish to define your own\nselection algorithm, you may extend `MyPDOMS` and override the\n[`getSlave`](https://github.com/DoctorMcKay/php-mypdoms/blob/cb03707ba87437192062931decd0ff6aa672a507/src/MyPDOMS.php#L200) method.\n\n# Prepared Statements\n\nBoth emulated and non-emulated prepared statements are fully supported, as they are assigned a connection at prepare-time.\nThat is, after a `PDOStatement` is returned from `prepare()`, the statement will always use the same database each time\nit is executed.\n\nNote: `lastUsedHost` is updated when a statement is *prepared*, but not when it is *executed*. This means that the\nfollowing is possible:\n\n```php\n\u003c?php\nuse Corn\\MyPDOMS\\MyPDOMS;\n\n$db = new MyPDOMS($dsn);\n$stmt = $db-\u003eprepare(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_1\n\n$db-\u003equery(\"SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_2\n\n$stmt-\u003eexecute(); // this is executed on slave_1 since it was prepared on slave_1\necho $db-\u003elastUsedHost . \"\\n\"; // slave_2\n\n$db-\u003equery(\"/*\" . MyPDOMS::HINT_LAST_USED . \"*/ SELECT 1\");\necho $db-\u003elastUsedHost . \"\\n\"; // slave_2\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoctormckay%2Fphp-mypdoms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoctormckay%2Fphp-mypdoms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoctormckay%2Fphp-mypdoms/lists"}