Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# basex-client-php
BaseX PHP Client

PSR Implementation of `Session` and `Query` classes from the main basex repository's PHP client code in `BaseXClient.php`: https://github.com/BaseXdb/basex/blob/master/basex-api/src/main/php/BaseXClient.php
PSR Implementation of `Session` and `Query` classes from the main basex repository's PHP client code in `BaseXClient.php`: https://github.com/BaseXdb/basex/tree/9/basex-api/src/main/php/BaseXClient
14 changes: 14 additions & 0 deletions src/BaseXException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
/*
* PHP client for BaseX.
* Works with BaseX 7.0 and later
*
* Documentation: https://docs.basex.org/wiki/Clients
*
* (C) BaseX Team 2005-22, BSD License
*/

namespace Caxy\BaseX;

class BaseXException extends \RuntimeException
{}
65 changes: 48 additions & 17 deletions src/Query.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
<?php
/*
* PHP client for BaseX.
* Works with BaseX 7.0 and later
*
* Documentation: https://docs.basex.org/wiki/Clients
*
* (C) BaseX Team 2005-22, BSD License
*/

namespace Caxy\BaseX;

use Exception;

class Query
class Query implements \Iterator
{
private $session;
private $id;
private $open;
private $cache;
protected $session;
protected $id;
protected $cache;
protected $pos;

public function __construct($s, $q)
/**
* Query constructor.
*
* @param Session $session
* @param string $query
*/
public function __construct($session, $query)
{
$this->session = $s;
$this->id = $this->exec(chr(0), $q);
$this->session = $session;
$this->id = $this->exec(chr(0), $query);
}

public function bind($name, $value, $type = "")
Expand All @@ -34,19 +46,20 @@ public function execute()

public function more()
{
if ($this->cache == NULL) {
if ($this->cache === null) {
$this->pos = 0;
$this->session->send(chr(4).$this->id.chr(0));
while (!$this->session->ok()) {
$this->cache[] = $this->session->readString();
}
if (!$this->session->ok()) {
throw new Exception($this->session->readString());
throw new BaseXException($this->session->readString());
}
}
if($this->pos < count($this->cache)) return true;
if ($this->pos < count($this->cache)) {
return true;
}
$this->cache = null;

return false;
}

Expand Down Expand Up @@ -76,10 +89,28 @@ public function exec($cmd, $arg)
{
$this->session->send($cmd.$arg);
$s = $this->session->receive();
if ($this->session->ok() != True) {
throw new Exception($this->session->readString());
if ($this->session->ok() !== true) {
throw new BaseXException($this->session->readString());
}

return $s;
}

public function current()
{
return $this->cache[$this->pos];
}

public function key()
{
return $this->pos;
}

public function valid()
{
return $this->more();
}

public function rewind()
{
}
}
105 changes: 80 additions & 25 deletions src/Session.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
<?php
/*
* PHP client for BaseX.
* Works with BaseX 7.0 and later
*
* Documentation: https://docs.basex.org/wiki/Clients
*
* (C) BaseX Team 2005-22, BSD License
*/

namespace Caxy\BaseX;

use Exception;

class Session
{
private $socket;
private $info;
private $buffer;
private $bpos;
private $bsize;
// instance variables.
protected $socket;
protected $info;
protected $buffer;
protected $bpos;
protected $bsize;

public function __construct($h, $p, $user, $pw)
public function __construct($hostname, $port, $user, $password)
{
// create server connection
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_connect($this->socket, $h, $p)) {
throw new Exception("Can't communicate with server.");
if (!socket_connect($this->socket, $hostname, $port)) {
throw new BaseXException("Can't communicate with server.");
}

// receive timestamp
Expand All @@ -26,51 +33,80 @@ public function __construct($h, $p, $user, $pw)
if (false !== strpos($ts, ':')) {
// digest-auth
$challenge = explode(':', $ts, 2);
$md5 = hash("md5", hash("md5", $user . ':' . $challenge[0] . ':' . $pw) . $challenge[1]);
$md5 = hash("md5", hash("md5", $user . ':' . $challenge[0] . ':' . $password) . $challenge[1]);
} else {
// Legacy: cram-md5
$md5 = hash("md5", hash("md5", $pw) . $ts);
$md5 = hash("md5", hash("md5", $password) . $ts);
}

// send username and hashed password/timestamp
socket_write($this->socket, $user . chr(0) . $md5 . chr(0));

// receives success flag
if (socket_read($this->socket, 1) != chr(0)) {
throw new Exception("Access denied.");
throw new BaseXException("Access denied.");
}
}

public function execute($com)
/**
* Execute BaseX command.
*
* @param string $command
* @return string
*/
public function execute($command)
{
// send command to server
socket_write($this->socket, $com.chr(0));
socket_write($this->socket, $command.chr(0));

// receive result
$result = $this->receive();
$this->info = $this->readString();
if ($this->ok() != True) {
throw new Exception($this->info);
if ($this->ok() != true) {
throw new BaseXException($this->info);
}

return $result;
}

public function query($q)
/**
* Execute XQuery query.
*
* @param string $xquery
* @return Query
*/
public function query($xquery)
{
return new Query($this, $q);
return new Query($this, $xquery);
}

/**
* Creates a new database, inserts initial content.
*
* @param string $name name of the new database
* @param string $input XML to insert
*/
public function create($name, $input)
{
$this->sendCmd(8, $name, $input);
}

/**
* Inserts a document in the database at the specified path.
*
* @param string $path filesystem-like path
* @param string $input XML to insert
*/
public function add($path, $input)
{
$this->sendCmd(9, $path, $input);
}

/**
* Replaces content at the specified path by the given document.
*
* @param string $path filesystem-like path
* @param string $input XML to insert
*/
public function replace($path, $input)
{
$this->sendCmd(12, $path, $input);
Expand All @@ -81,11 +117,19 @@ public function store($path, $input)
$this->sendCmd(13, $path, $input);
}

/**
* Status information of the last command/query.
*
* @return string|null
*/
public function info()
{
return $this->info;
}

/**
* Close the connection.
*/
public function close()
{
socket_write($this->socket, "exit".chr(0));
Expand All @@ -98,13 +142,16 @@ private function init()
$this->bsize = 0;
}

/**
* @internal
* @return string
*/
public function readString()
{
$com = "";
while (($d = $this->read()) != chr(0)) {
$com .= $d;
}

return $com;
}

Expand All @@ -114,16 +161,15 @@ private function read()
$this->bsize = socket_recv($this->socket, $this->buffer, 4096, 0);
$this->bpos = 0;
}

return $this->buffer[$this->bpos++];
}

private function sendCmd($code, $arg, $input)
{
socket_write($this->socket, chr($code).$arg.chr(0).$input.chr(0));
$this->info = $this->receive();
if ($this->ok() != True) {
throw new Exception($this->info);
if ($this->ok() != true) {
throw new BaseXException($this->info);
}
}

Expand All @@ -132,15 +178,24 @@ public function send($str)
socket_write($this->socket, $str.chr(0));
}

/**
* Was the last command/query successful?
*
* @internal not idempotent, not intended for use by client code
* @return bool
*/
public function ok()
{
return $this->read() == chr(0);
}

/**
* @internal
* @return string
*/
public function receive()
{
$this->init();

return $this->readString();
}
}