[TASK] Restructured parts of the core to support sending data right after connecting. [TASK] Adapted core part for automatic reconnect to support sending initial data, too.
		
			
				
	
	
		
			305 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/**
 | 
						|
 * This class contains the connection handler.
 | 
						|
 * It handles all the incoming and outgoing data by forwarding it
 | 
						|
 * to the clients for the specific connection.
 | 
						|
 * @author jpt
 | 
						|
 * @package Client
 | 
						|
 * @depends Connection
 | 
						|
 * TODO: implement routing with some more detailed rules, remove hardcoded client-based routing.
 | 
						|
 */
 | 
						|
class Client_ClientManager {
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @var Connection_ConnectionPool
 | 
						|
	 */
 | 
						|
	protected $connectionPool;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * An array that contains all client instances for each connection.
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $clientPool;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $routingRules;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * An array that contains all protocols we have clients for.
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $registeredProtocols;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Contains Connection-attached configurations.
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $configPool;
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Default constructor.
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function __construct() {
 | 
						|
		$this->connectionPool = new Connection_ConnectionPool();
 | 
						|
		$this->clientPool = array();
 | 
						|
		$this->registeredProtocols = array();
 | 
						|
		$this->routingRules = array();
 | 
						|
		$this->configPool = array();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Default destructor. Closes connections before being killed.
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function __destruct() {
 | 
						|
		unset($this->clientPool);
 | 
						|
		unset($this->connectionPool);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @see Connection_ConnectionPool
 | 
						|
	 * @return int
 | 
						|
	 */
 | 
						|
	public function countSockets() {
 | 
						|
		return $this->connectionPool->countSockets();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns the amount of connected connection handlers.
 | 
						|
	 * @see Connection_ConnectionPool
 | 
						|
	 * @return int
 | 
						|
	 */
 | 
						|
	public function countActiveConnections() {
 | 
						|
		return $this->connectionPool->countActiveConnections();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Main worker function.
 | 
						|
	 * Processes incoming data, calls clients and much more.
 | 
						|
	 * TODO: refactor this? (split it into handleFoobar() functions)
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function work() {
 | 
						|
			//firstly, create clients for connections without one.
 | 
						|
		foreach($this->connectionPool->getConnectionHandlers() AS $connectionHandler) {
 | 
						|
			if(!isset($this->clientPool[$connectionHandler->getID()])) {
 | 
						|
					//new connections might need a client, so we'll create one here.
 | 
						|
				$this->addClientForConnectionHandler($connectionHandler);
 | 
						|
					//allow client to send initial stuff right after connecting to the server.
 | 
						|
				$this->clientPool[$connectionHandler->getID()]->initializeConnection();
 | 
						|
					//after initializing, have it process an empty string in order to get stuff to write. >.<
 | 
						|
				$result = $this->clientPool[$connectionHandler->getID()]->processRawData("");
 | 
						|
				if($result !== "") $connectionHandler->write($result);
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
			//then, process all connections that have stuff to read.
 | 
						|
		$connectionHandlers = $this->connectionPool->select();
 | 
						|
		if(isset($connectionHandlers["read"]) === FALSE || count($connectionHandlers["read"]) === 0) return;
 | 
						|
		foreach($connectionHandlers["read"] AS $connectionHandler) {
 | 
						|
				//handle disconnects
 | 
						|
			if($connectionHandler->isConnected() === FALSE && $connectionHandler->isListening() === FALSE) {
 | 
						|
					//check whether the reconnect-flag is set
 | 
						|
				if($connectionHandler->getReconnect() === TRUE) {
 | 
						|
						//have the connectionHandler do the reconnect.
 | 
						|
					$newConnectionHandler = $connectionHandler->reconnect();
 | 
						|
						//get the client and reset it.
 | 
						|
					$client = $this->clientPool[$connectionHandler->getID()];
 | 
						|
					$client->resetConnectionStatus();
 | 
						|
						//assign the client to the new connectionHandler.
 | 
						|
					$this->clientPool[$newConnectionHandler->getID()] = $client;
 | 
						|
						//get the config and assign it to the new connectionHandler, too.
 | 
						|
					$config = $this->configPool[$connectionHandler->getID()];
 | 
						|
					$this->configPool[$newConnectionHandler->getID()] = $config;
 | 
						|
						//remove old connection
 | 
						|
					$this->removeConnection($connectionHandler);
 | 
						|
						//re-initialize the client
 | 
						|
					$this->clientPool[$newConnectionHandler->getID()]->initializeConnection();
 | 
						|
						//and of course, process stuff to get the buffer filled. :/
 | 
						|
					$result = $this->clientPool[$newConnectionHandler->getID()]->processRawData("");
 | 
						|
					if($result !== "") $newConnectionHandler->write($result);
 | 
						|
				}
 | 
						|
				$this->removeClientForConnectionHandler($connectionHandler);
 | 
						|
					//this connectionHandler won't do much anymore - kill it.
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
				//handle accepted sockets, adopt them and treat them with care :-)
 | 
						|
			if($connectionHandler->isListening() === TRUE) {
 | 
						|
				$this->addClientForConnectionHandler($connectionHandler);
 | 
						|
					//TODO: maybe we want to trigger the "client" here, too? -- YES
 | 
						|
				$this->clientPool[$connectionHandler->getID()]->initializeConnection();
 | 
						|
			}
 | 
						|
 | 
						|
				//call the registered client
 | 
						|
			if(isset($this->clientPool[$connectionHandler->getID()])) {
 | 
						|
					//let the client process the data, send the results back.
 | 
						|
				while($data = $connectionHandler->read()) {
 | 
						|
					$result = $this->clientPool[$connectionHandler->getID()]->processRawData($data);
 | 
						|
					if($result !== "") {
 | 
						|
						$connectionHandler->write($result);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
			//after that, we're done here.
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Calls ConnectionPool in order to create a simple connection.
 | 
						|
	 * @see Connection_ConnectionPool
 | 
						|
	 * @param string $group
 | 
						|
	 * @param boolean $IPv6
 | 
						|
	 * @return Connection_ConnectionHandler
 | 
						|
	 */
 | 
						|
	public function createTcpConnection($group = "", $protocol = "RAW", $IPv6 = FALSE) {
 | 
						|
		return $this->connectionPool->createTcpConnection($group, $protocol, $IPv6);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Closes and removes the connection.
 | 
						|
	 * @param Connection_ConnectionHandler $connectionHandler
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function removeConnection($connectionHandler) {
 | 
						|
		unset($this->configPool[$connectionHandler->getID()]);
 | 
						|
		unset($this->clientPool[$connectionHandler->getID()]);
 | 
						|
		$this->connectionPool->removeConnectionHandler($connectionHandler);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Registers a protocol with a specific client.
 | 
						|
	 * @param string $protocol
 | 
						|
	 * @param string $className
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function registerProtocol($protocol, $classNamePartial) {
 | 
						|
		$this->registeredProtocols[$protocol] = $classNamePartial;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Unregisters a protocol.
 | 
						|
	 * @param string $protocol
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function unregisterProtocol($protocol) {
 | 
						|
		unset($this->registeredProtocols[$protocol]);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Searches for the registered client for the given protocol.
 | 
						|
	 * Returns a client instance or void.
 | 
						|
	 * @param string $protocol
 | 
						|
	 * @return mixed
 | 
						|
	 * @throws Exception_GeneralException
 | 
						|
	 */
 | 
						|
	protected function createClientForProtocol($protocol) {
 | 
						|
			//look for the protocol
 | 
						|
		if(!in_array($protocol, $this->registeredProtocols)) {
 | 
						|
			throw new Exception_GeneralException("No client registered for protocol: '" . $protocol . "'!", 1290271651);
 | 
						|
		}
 | 
						|
		$className = "Client_" . $protocol . "Client";
 | 
						|
			//look for the class
 | 
						|
		if(class_exists($className)) {
 | 
						|
			$client = new $className();
 | 
						|
			if(!$client instanceof Client_AbstractClient) throw new Exception_GeneralException("Class '" . $className . "' does not implement the AbstractClient-API!", 1294837055);
 | 
						|
			return $client;
 | 
						|
		} else {
 | 
						|
			throw new Exception_GeneralException("Registered class name '" . $className . "' does not exist for protocol: '" . $protocol . "'!", 1290271773);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Adds a client to our client pool.
 | 
						|
	 * @param Connection_ConnectionHandler $connectionHandler
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	protected function addClientForConnectionHandler($connectionHandler) {
 | 
						|
		$protocol = $this->registeredProtocols[$connectionHandler->getProtocol()];
 | 
						|
			//do not try this for raw connections
 | 
						|
		if($protocol === "RAW") return;
 | 
						|
		$client = $this->createClientForProtocol($protocol);
 | 
						|
		if(isset($this->configPool[$connectionHandler->getID()])) {
 | 
						|
			$client->loadConfig($this->configPool[$connectionHandler->getID()]);
 | 
						|
		}
 | 
						|
		$client->injectClientManager($this);
 | 
						|
		$protocolHandlerClass = "Protocol_".$protocol."ProtocolHandler";
 | 
						|
		$protocolHandler = new $protocolHandlerClass();
 | 
						|
		$client->injectProtocolHandler($protocolHandler);
 | 
						|
		$client->setID($connectionHandler->getID());
 | 
						|
		$client->setGroup($connectionHandler->getGroup());
 | 
						|
		$this->clientPool[$connectionHandler->getID()] = $client;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Removes a client from our client pool.
 | 
						|
	 * @param Connection_ConnectionHandler $connectionHandler
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	protected function removeClientForConnectionHandler($connectionHandler) {
 | 
						|
		unset($this->clientPool[$connectionHandler->getID()]);
 | 
						|
		$this->connectionPool->removeConnectionHandler($connectionHandler);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Attaches a configuration to a connection.
 | 
						|
	 * Will overwrite the existing configuration for the connection.
 | 
						|
	 * @param array $config
 | 
						|
	 * @param Connection_ConnectionHandler $connectionHandler
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function attachConfig($config, $connectionHandler) {
 | 
						|
		$this->configPool[$connectionHandler->getID()] = $config;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Calls Connection_Pool
 | 
						|
	 * @see Connection_ConnectionPool
 | 
						|
	 * @param int $id
 | 
						|
	 * @param string $data
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function sendToID($id, $data) {
 | 
						|
		return $this->connectionPool->writeToID($id, $data);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns a list of available connection IDs.
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function getIDs() {
 | 
						|
		$list = array();
 | 
						|
		foreach($this->connectionPool->getConnectionHandlers() AS $connectionHandler) {
 | 
						|
			$list[] = $connectionHandler->getID();
 | 
						|
		}
 | 
						|
		return $list;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns a list of available connection groups.
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	public function getGroups() {
 | 
						|
		$list = array();
 | 
						|
		foreach($this->connectionPool->getConnectionHandlers() AS $connectionHandler) {
 | 
						|
			$list[] = $connectionHandler->getGroup();
 | 
						|
		}
 | 
						|
		return $list;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Calls Connection_Pool
 | 
						|
	 * @see Connection_ConnectionPool
 | 
						|
	 * @param string $group
 | 
						|
	 * @param string $data
 | 
						|
	 * @return void
 | 
						|
	 */
 | 
						|
	public function sendToGroup($group, $data) {
 | 
						|
		return $this->connectionPool->writeToGroup($group, $data);
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
?>
 |