PHP SQLクエリ

画像



ORMはニーズに対して重すぎるため、通常はDbSimpleを使用しました 。 しかし、phpコードでテンプレートをコンパイルするTwigと会った後、データベースを操作するために似たようなものを書くというアイデアが定期的に生まれました。 そして、私はそれをやった。 この写真は、コンパイル後にSQLクエリを作成および実行するコードを生成するPHPのリクエストを示しています。



最初の実装では、PHPコードのDbSimpleに似た構文からリクエストをコンパイルしました。 アイデアは、ラッパーなしで出力にネイティブ関数を備えた既製のコードを取得することでした。 同時に、あらゆる複雑なクエリを作成することが可能であり、コンパイル後は通常のネイティブコードであるため、分析の速度は操作時間に影響しませんでした。 ただし、このようなクエリのデバッグの難しさ(SQL構文のエラーを探すのは困難でした)と、クエリの解析時間がクエリの実行に比べてそれほど長くないという事実から、このアプローチを使用するという考えを捨てました。



少し前、 PHP-ParserトークンでPHPコードを解析するためのライブラリに出会いました。 職場では、データベースを操作するためのコマンドが言語自体に組み込まれているABAP言語でコードを記述しているため、「PHPでこのようなことをしたらどうなるか」というアイデアが浮上しました。



実装スキームは非常にシンプルで明白です。クラスが自動ロードされると、コンパイルされたクラスのディレクトリでその存在をチェックします。 コンパイル済みクラスがない場合は、クラスのソースファイルを取得し、その中のすべての特別なコマンドを置き換え、完成したクラスをコンパイル済みクラスのディレクトリに書き込みます。 解析全体は、PHP-Parserライブラリによって行われます。 コンパイラがこれが必要なコマンドであることを理解できるように、すべてをML名前空間( M acro L anguage)でラップします。 たとえば、コードでは次のように記述します。



\ML::SQL(Select( 'aa,bb', ucase('xx')->as('Uxx') ), from("MyTable")->as("tab"), where( like('aa','sha%'), _or( field('bb') == NULL, field('bb') == 2006 ) ), orderBy( "-aa,+bb" ), into($rows)->rows());
      
      





出力が得られます:



 $__driver0 = \ML\SQL::getDriver('c933f3523437d521bf59e9e6077255b9', array('server' => '***', 'database' => '***', 'user' => '***', 'pass' => '***', 'prefix' => 'MyCMS-', 'codepage' => 'utf8')); $__query1 = new \ML\SQL\Query($__driver0); $__query1->sql = 'SELECT `tab`.`aa` as `tab.aa`, `tab`.`bb` as `tab.bb`, UCASE(`tab`.`xx`) as `Uxx` FROM `MyCMS-MyTable` as `tab` WHERE `tab`.`aa` LIKE \'sha%\' AND (`tab`.`bb` IS NULL OR `tab`.`bb`=2006) ORDER BY `tab`.`aa` DESC, `tab`.`bb` ASC'; $rows = $__query1->rows();
      
      





同時に、PHP変数をクエリ( PHPおよびMySQLでのSQLインジェクションに対する保護を備えたクエリに置き換えられます )および条件付きクエリ生成_ifの関数で直接使用できます。 特に、次のコード:



 \ML::SQL(Select( 'aa', _if($mode==1,'bb') ), from("MyTable")->as("tab"), where( field($fname) = $value ), into($rows)->row());
      
      





次のようなコードにコンパイルします。



 $__driver0 = \ML\SQL::getDriver('c933f3523437d521bf59e9e6077255b9', array('server' => '***', 'database' => '***', 'user' => '***', 'pass' => '***', 'prefix' => 'MyCMS-', 'codepage' => 'utf8')); $__query1 = new \ML\SQL\Query($__driver0); $__query1->sql = 'SELECT `tab`.`aa` as `tab.aa`' . ($mode == 1 ? ', `tab`.`bb` as `tab.bb`' : '') . ' FROM `MyCMS-MyTable` as `tab` WHERE ' . $__driver0->getField('tab', $fname, '', '') . '=' . $__driver0->getValue($value); $rows = $__query1->row();
      
      





実装スキームは単純なので、SQLだけに限定しないことにしました。 これを行うために、コードは特定の\ ML \ SQLコマンドではなく、\ ML名前空間からのすべての呼び出しを探します。 そのような呼び出しが見つかった場合、クラス\ ML \ <クラス名>の存在がチェックされます。 そのようなクラスがある場合、このクラスのオブジェクトが作成され、そのメソッド<クラス名>->コンパイル(...)が呼び出され、現在の呼び出し( PHP-Parserから受け取ったルートノード)のトークン+このクラスのオブジェクトが渡されます。 これにより、データベースへのアクセスだけでなく、他のニーズにもこのアプローチを使用できるようになります(まだ思いついていません:))。 特に、SQLでORM拡張を行う予定です。 つまり \ ML \ ORM呼び出しを\ ML \ SQLコードにコンパイルします。これは既にPHPコードにコンパイルされています。 さらに、式の解析時間は重要ではないため、ORM呼び出しを解析する場合、エンティティ、それらの関係、フィールドなどに関する情報の読み取りに時間を費やすことができます。



PS ORMを実行する前に、JOINコマンド+ UPDATEおよびDELETEコマンドを追加します。



興味がある人のために、コードをコンパイルするための小さなテストサイト 。 間違いを見つけたら、コメントを書いてください。 これまでのところ、これはバージョン0.1アルファであるため、エラーが発生する可能性が非常に高くなります。



UPD:2017.02.03さらに例を追加



UPD: 次のバージョン



All Articles