Async MySQL Framework for PocketMine-MP

Overview

MyPigSQL

Join my discord: https://discord.gg/2QAPHbqrny

Async MySQL Framework for PocketMine-MP

Known Issues

  • There are some weird behaviors still, do not use this for production server until further notice. I will be testing it on a network first and fix all possible issues.
Feature State
MultiThreaded System ✔️
SQLConnString Object ✔️
SQLRequest Object ✔️
SQLRequestExceptions ✔️
SQLRequest Completions ✔️

Prerequisites

  • Working MySQL Server.

Why is this necessary?

Unlike other SQL libraries for PMMP, you don't need to specify your queries in another file. You can create your queries on the fly while keeping a strict environment. This library also makes it possible to use multiple connections so you can interact with multiple databases & tables. The creation of the SQLConnStrings & SQLRequests are very similar to how you create packets in PMMP.

Note that every queries are prepared and you must supply the data types & the data variables (as array) when creating a new SQLRequest.

SQLConnString Creation

In order to send requests to the MySQL servers, you will surely need to create the SQLConnStrings which are the informations needed for the DispatchBatchThread to execute the queries.

# This is how to create a new SQLConnString from the SQLConnString::create() function.
$sqlConn = SQLConnString::create(
        'MainDatabase', # Name of the SQLConnString.
        '127.0.0.1', # host/address of the server.
        'mainUser', # Username to connect.
        'mainUserPassword', # User password.
        'notifications', # Database used.
        3306, # Port of the server, set to 3306 by defaulté
        true); # If has to validate. Is set to true by default.

# This is how to create it from the functions within it.
$connString = new SQLConnString();
$connString->setName('MainDatabase');
$connString->setAddress('127.0.0.1');
$connString->setUsername('mainUser');
$connString->setPassword('mainUserPassword');
$connString->setDatabase('notifications');
$connString->setPort(3589); # 3306 is the default port if none is supplied.
$connString->validate();

There are quite a few variables you can give to the SQLConnString object. Here's a table of the different variables.

Property DataType Description
$name string The name of the connection.
$address string The host/address of the server server.
$username string The username used to connect.
$password string The password of the user.
$database string The database used.
$port int The port of the server.

SQLConnString::validate();

This function will validate the connection when created. If there is a problem, the function will throw a SQLRequestException with the message from the mysqli connection error. It is not obligated to validate a connection if you know that it will work but note that if you use SQLConnString::create(), you must specify at the end with a false.

How to work with SQLConnString?

When you successfully created your SQLConnString object & validated (optional), you will need to register it to the list of SQLConnStrings, for that, you simple need to use one line of code, here it is :

MyPigSQL::registerStringConn($connString); # Returns true if successful

This function will throw a SQLRequestException if you try to register two SQLConnStrings with the same name ($connString->getName()) If for some reason you need to also unregister it, you can use this function:

MyPigSQL::unregisterStringConn($connString); # Returns true if successful

Here is the list of functions related to the SQLConnString object.

MyPigSQL::getSQLConnStringByName($connName); # Returns SQLConnString object or throws SQLRequestException.
MyPigSQL::unregisterStringConn($SQLConnString); # Unregisters the SQLConnString object (must be supplied with the object).
MyPigSQL::unregisterStringConnByName($SQLConnStringName); # Unregisters the SQLConnString object by name.
MyPigSQL::registerStringConn($connString); # Registers the SQLConnString (must be supplied with the object).

# ['address' => $address, 'username' => $user, 'password' => $password, 'database' => $database, 'port' => $port]
MyPigSQL::validateConnString($connStringData); # Will validate the data to create a SQLConnString.

SQLRequest Creation

When you are done with creating & registering your SQLConnStrings, you will finally be able to create new SQLRequests. The way you make them is exactly the same as how you create SQLConnStrings. You can use SQLRequest::create() or $request = new SQLRequest(). Here are two examples:

getName()}, you have {$data['kills'] kills!}"); } ); # This is how to create it from the functions within it. $request = new SQLRequest(); $request->setQuery("SELECT * FROM FriendRequests WHERE id = ?"); $request->setDataTypes('s'); $request->setDataKeys([$player->getUniqueId()->toString()]); $request->setConnString(MyPigSQL::getSQLConnStringByName('MainDatabase')); $request->setCallable(function(array $data) use ($player){ $player->sendMessage->('Wow, this is very fine! Here is when the relation has been created: ' . $data['reg_date']); }); # Here is a real-world example of a fully implemented command using MyPigSQL. /** * @param CommandSender $sender * @param string $commandLabel * @param array $args * @throws SQLRequestException */ public function execute(CommandSender $sender, string $commandLabel, array $args): void { if (!$sender->hasPermission(DefaultPermissions::ROOT_OPERATOR)) { $sender->sendMessage(Translation::getMessage('noPermission')); return; } if (!isset($args[1])) { $sender->sendMessage(Translation::getMessage('usageMessage', [ 'usage' => $this->getUsage() ])); return; } if (!is_numeric($args[1])) { $sender->sendMessage(Translation::getMessage('notNumeric')); return; } $player = $this->getCore()->getServer()->getPlayerExact($args[0]); MyPigSQL::addQueryToBatch(SQLRequest::create( 'UPDATE players SET shards = shards + ? WHERE username = ?', 'ss', [$args[1], $args[0]], MyPigSQL::getSQLConnStringByName('mainDB'), function(?array $data) use ($sender, $player, $args){ $name = $args[0]; if($player instanceof CorePlayer){ $player->addShards((int)$args[1]); $name = $player->getName(); } $sender->sendMessage(Translation::getMessage('addShardsSuccess', [ 'amount' => TF::GREEN . '' . $args[1], 'name' => TF::GOLD . $name ])); } )); }">
# This is how to create a new Utils from the Utils::create() function.
$requestTwo = SQLRequest::create(
        'SELECT * FROM PlayerData WHERE xuid = ?',
        's',
        [$player->getUniqueId()->toString()],
        self::getSQLConnStringByName('MainDatabase'),
        function (array $data) use ($player){
            $player->sendMessage("Welcome {$player->getName()}, you have {$data['kills'] kills!}");
        }
);
# This is how to create it from the functions within it.
$request = new SQLRequest();
$request->setQuery("SELECT * FROM FriendRequests WHERE id = ?");
$request->setDataTypes('s');
$request->setDataKeys([$player->getUniqueId()->toString()]);
$request->setConnString(MyPigSQL::getSQLConnStringByName('MainDatabase'));
$request->setCallable(function(array $data) use ($player){
        $player->sendMessage->('Wow, this is very fine! Here is when the relation has been created: ' . $data['reg_date']);
});

# Here is a real-world example of a fully implemented command using MyPigSQL.
    /**
     * @param CommandSender $sender
     * @param string $commandLabel
     * @param array $args
     * @throws SQLRequestException
     */
    public function execute(CommandSender $sender, string $commandLabel, array $args): void
    {
        if (!$sender->hasPermission(DefaultPermissions::ROOT_OPERATOR)) {
            $sender->sendMessage(Translation::getMessage('noPermission'));
            return;
        }
        if (!isset($args[1])) {
            $sender->sendMessage(Translation::getMessage('usageMessage', [
                'usage' => $this->getUsage()
            ]));
            return;
        }
        if (!is_numeric($args[1])) {
            $sender->sendMessage(Translation::getMessage('notNumeric'));
            return;
        }

        $player = $this->getCore()->getServer()->getPlayerExact($args[0]);
        MyPigSQL::addQueryToBatch(SQLRequest::create(
            'UPDATE players SET shards = shards + ? WHERE username = ?',
            'ss',
            [$args[1], $args[0]],
            MyPigSQL::getSQLConnStringByName('mainDB'),
            function(?array $data) use ($sender, $player, $args){
                $name = $args[0];
                if($player instanceof CorePlayer){
                    $player->addShards((int)$args[1]);
                    $name = $player->getName();
                }
                $sender->sendMessage(Translation::getMessage('addShardsSuccess', [
                    'amount' => TF::GREEN . '' . $args[1],
                    'name' => TF::GOLD . $name
                ]));
            }
        ));
    }

There are quite a few variables you can give to the SQLRequest object. Here's a table of the different variables.

Property DataType Description
$query string The query string.
$dataTypes string The variable types used in the prepared statement.
$dataKeys array The variables used in the prepared statement.
$connString SQLConnString The SQLConnString object used for the request.
$validated bool If the request has been sent to the MySQL server.
$id string The id of the request.

To register your query in the next batch of queries to be sent, you need to use this simple line of code:

MyPigSQL::addQueryToBatch($request); # NOTE: MIGHT BE CHANGED TO addRequestToBatch().

Here is a list of the other functions related to SQLRequests.

MyPigSQL::addQueryToBatch($request); # Will register the request to the batch.
MyPigSQL::removeQueryFromBatch($id); # Will unset the request from the batch, must be supplied with the request's id.
MyPigSQL::getQueryFromBatch($id); # Will return the Utils object from the batch, must be supplied with the request's id.

Notes

  1. MyPigSQL::addQueryToBatch($request); might be changed to MyPigSQL::addRequestToBatch($request);.
  2. SQLRequest::setDataKeys($dataList); might be changed to SQLRequest::setDataInput($dataList);.
  3. You can, if you do not want to register a new SQLConnString, create an SQLConnStrig without a $name (SQLConnString::setName($name)) by making it null when creating it.
  4. A thread for each SQLConnStrings might be implemented to speed up the execution of the batch.

Why this?

I made this plugin in my spare time because I didn't want to use another library for my network and because I am planning to connect all my other systems (PigNotify, PigFriend & more to come) "together". My only problem was the use of threads for each plugins so this library fixes the issue by using one thread for all other systems.

You might also like...
phpMyFAQ - Open Source FAQ web application for PHP and MySQL, PostgreSQL and other databases

phpMyFAQ 3.1 What is phpMyFAQ? phpMyFAQ is a multilingual, completely database-driven FAQ-system. It supports various databases to store all data, PHP

A simple and extensible fixture loader for PHP 7.3+, supporting SQLite and MySQL

Flowder Flowder is a (really) simple fixture loader for PHP 7.3+, supporting SQLite and MySQL. Using Flowder in PHP 7.2 or below? Try version 1 instea

A mysql database backup package for laravel

Laravel Database Backup Package This package will take backup your mysql database automatically via cron job. Installing laravel-backup The recommende

Laravel Code Generator based on MySQL Database

Laravel Code Generator Do you have a well structed database and you want to make a Laravel Application on top of it. By using this tools you can gener

A Symfony application for managing and automating regular backups of MySQL databases.
A Symfony application for managing and automating regular backups of MySQL databases.

DbSaver DbSaver is an application written by Bastien LOUGHIN allowing you to make automatic daily backups (and manual backups) for your MySQL database

TO DO LIST WITH LOGIN AND SIGN UP and LOGOUT using PHP and MySQL please do edit the _dbconnect.php before viewing the website.

TO-DO-LIST-WITH-LOGIN-AND-SIGN-UP TO DO LIST WITH LOGIN AND SIGN UP and LOGOUT using PHP and MySQL please do edit the _dbconnect.php before viewing th

Connect and work with MySQL/MariaDB database through MySQLi in PHP. This is an introductory project, If you need a simple and straightforward example that takes you straight to the point, you can check out these examples.
Connect and work with MySQL/MariaDB database through MySQLi in PHP. This is an introductory project, If you need a simple and straightforward example that takes you straight to the point, you can check out these examples.

First MySQLi PHP Connect and work with MySQL/MariaDB database through MySQLi in PHP. The above exercises are designed for students. This is an introdu

Very easy to use PDO MYSQL API. Just Include in PHP file and get it working.

CRUD-MYSQL-API Very easy to use PDO MYSQL API. Just Include in PHP file and get it working. INSTALATION Step 1: git clone https://github.com/arhex-lab

Staggered import of large and very large MySQL Dumps even through the web servers with hard runtime limit and those in safe mode.

Staggered import of large and very large MySQL Dumps (like phpMyAdmin dumps) even through the web servers with hard runtime limit and those in safe mode. | Persian Translation Version

Comments
  • Dispatchpool

    Dispatchpool

    Major changes

    • New DispatchBatchPool class.
    • Now, the user can set a maximum requests per batch (20 recommended). When exceeded, the requests will jump to a second batch that will be executed on another thread.
    • New DispatchBatchThread types (HELP_THREAD & MAIN_THREAD).

    SQLRequest methods changes

    • SQLRequest::isExecuted() has been renamed SQLRequest::hasBeenDispatched().
    • Added SQLRequest::setDispatched($value).
    • Added SQLRequest::hasBeenCompleted().
    • Added SQLRequest::setCompleted($value).

    API Changes

    • MyPigSQL::removeQueryFromBatch($id) has been changed to MyPigSQL::removeQueryFromBatch($id, $batch.

    DispatchBatchPool

    This class extends Pool and has, for now, no methods. But the end goal will be to let users create their own batches of requests and send them over a custom DispatchBatchPool where they will be able to set a batch, the amount of DispatchBatchThread they want in them and other few options. The batch will be divided by the amount of DispatchBathThread the user set.

    More...

    Config.yml

    • Now, you can set the request-per-batch which will limit the amount of SQLRequests in a single batch. 20 is the default value and should be kept that way. If your batch-update-interval is set to 2 seconds, it means that in order to create a new batch, you would need to have more more than 20 SQLRequests per 2 seconds.

    Current Bugs

    • There are zombie SQLRequest that can stay in the batches but they won't be executed multiple times and are extremely rare.
    • The repeating ClosureTask task taking care of firing up the DispatchBatchThreads and preparing the requests isn't optimized and can overload the main thread if there are too many requests. This has been observed when creating a request every 1-3 ticks.
    • For some reason, SQLRequest::setDispatched() & SQLRequest::setCompleted() will not set the value to true if the developer passes no variable to them (they are using bool $value = true when passing no variable to them.

    Next for 3.0.0-beta

    • Optimization of the preparation of the ``SQLRequests` in the ClosureTask.
    • Addition of the missing methods from the DispatchBatchPool.
    • SQLRequest priority system where certain requests will wait for x request to be completed before being dispatched.
    • Adding method SQLRequest::waitFor(string $id).
    opened by CupidonSauce173 0
  • High use of SQLRequests will make DispatchBatchThread slower.

    High use of SQLRequests will make DispatchBatchThread slower.

    A potential problem with the current system is that the DispatchBatchThread will get slower if lot of plugins are using it / high requests demand. One way to fix this would be to create a ThreadPool with multiple DispatchBatchThread classes and set a maximum of requests per batch in the config.yml, when the limit exceeds, the plugin will create a new DispatchBatchThread with a new request batch to be executed and then closed.

    The problem from this is that a poor designed plugin could make it so requestX would be executed before requestY, even if the best would be to create a multidimensional SQLRequest structure where an SQLRequest would be created after requestX and requestY within their completion function.

    A way to fix it would be to wait for requestX to be "validated" SQLRequest::hasBeenExecuted() or a new method SQLRequest::hasBeenCompleted could be added (since SQLRequest::hasBeenExecuted() is only if the request has been sent to the DisptachBatchThread).

    So a new variable could be added on SQLRequest::create() where a developer can specify a certain ID to be waited for (when dispatching the batch across the threads, it would look if the request has to wait for a certain SQLRequest::getId() to be completed.

    SQLRequest additions

    • SQLRequest::hasBeenCompleted(): bool, Check if the request has executed the callable function.
    • SQLRequest::waitForRequest(string $id): void wait for requestX to be completed before executing it.
    • SQLRequest::getWaitingRequest() : ?string Returns the request that this request is waiting for to be completed.

    Potential SQLRequest changes

    • SQLRequest::hasBeenExecuted() renamed to SQLRequest::hasBeenDispatched().

    Config.yml additions

    • batch-maximum-requests, Define the maximum SQLRequests that a batch can contain before starting up a new DispatchBatchThread.

    Core changes

    • There will be a new dispatchBatchThreadPool managing multiple dispatchBatchThread instances and closing them when not needed (and only keeping one alive).

    See the branch : dispatchpool

    enhancement 
    opened by CupidonSauce173 1
Releases(beta)
Owner
Antoine
Mainly on PHP & MySQL Also do some C# & Java. Weird fetish for creating APIs and GitHub README.md
Antoine
Async Redis client implementation, built on top of ReactPHP.

clue/reactphp-redis Async Redis client implementation, built on top of ReactPHP. Redis is an open source, advanced, in-memory key-value database. It o

Christian Lück 240 Dec 20, 2022
The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

Mark Armendariz 0 Jan 7, 2022
Independent query builders for MySQL, PostgreSQL, SQLite, and Microsoft SQL Server.

Aura.SqlQuery Provides query builders for MySQL, Postgres, SQLite, and Microsoft SQL Server. These builders are independent of any particular database

Aura for PHP 424 Dec 12, 2022
A web interface for MySQL and MariaDB

phpMyAdmin A web interface for MySQL and MariaDB. https://www.phpmyadmin.net/ Code status Download You can get the newest release at https://www.phpmy

phpMyAdmin 6.4k Jan 5, 2023
A validating SQL lexer and parser with a focus on MySQL dialect.

SQL Parser A validating SQL lexer and parser with a focus on MySQL dialect. Code status Installation Please use Composer to install: composer require

phpMyAdmin 368 Dec 27, 2022
PHP version of mysqldump cli that comes with MySQL

MySQLDump - PHP Requirements | Installing | Getting started | API | Settings | PDO Settings | TODO | License | Credits This is a php version of mysqld

diego torres 1.1k Jan 8, 2023
MySQL Spatial Data Extension integration with Laravel.

Laravel MySQL Spatial extension Laravel package to easily work with MySQL Spatial Data Types and MySQL Spatial Functions. Please check the documentati

Joseph Estefane 741 Jan 9, 2023
Symfony 5.2 + api platform project with ELK stack + elastic FileBeats for the log management. All running in 7 docker containers: nginx, php 8, mysql, elastic search, logstash, kibana, fileBeats.

Symfony with ELK and Elastic FileBeats Stack Prerequisites: Make sure that docker and docker-compose are installed in your machine and available for y

null 13 May 20, 2022
MySQL Load Data Infile Support For Laravel

Laravel Load File ?? A package to help with loading files into MySQL tables. This uses MySQL's LOAD DATA statement to load text files quickly into you

Ellis Green 64 Dec 30, 2022
A simple PHP and MySQL based internet forum that displays the messages in classical threaded view (tree structure)

my little forum my little forum is a simple PHP and MySQL based internet forum that displays the messages in classical threaded view (tree structure).

Mark Hoschek 97 Dec 29, 2022