<?php

class ChatServer 
{

   private $ipAddress = null;
   private $port = null;

   private $masterClient = null;
   private $clients;

   // Komunikaty serwera	
   private static $SOCKET_CREATE_FAIL = "Nie powiodło się wykonanie funkcji socket_create()";
   private static $SOCKET_BIND_FAIL = "Nie powiodło się wykonanie funkcji socket_bind()";
   private static $SOCKET_LISTEN_FAIL = "Nie powiodło się wykonanie funkcji socket_listen()";
   private static $SOCKET_ACCEPT_FAIL = "Nie powiodło się wykonanie funkcji socket_accept()";

   private $LINE_ENDING = "\n";
   private static $SHOW_ERRORS = 1; // Wyświetlanie błędów (0=wyłącz, 1=włącz)

   function ChatServer($ip='192.168.1.110', $port=8888)
   {
      $this->ipAddress = $ip;
      $this->port = $port;

      $this->clients = array();
   }

   public function startServer()
   {
      $this->createMasterClient();
      $this->initLoop();
   }

   public function stopServer()
   {

   }

   private function initLoop()
   {

      $socketsChanged = null;

      while (true) 
      {
         $socketsChanged = $this->clients;

         //print_r($socketsChanged);

         $write  = NULL;
         $except = NULL;

         socket_select($socketsChanged, $write, $except, NULL);

         foreach ($socketsChanged as $socket) 
         {
            if ($socket == $this->masterClient)
            {
               if (($client = socket_accept($this->masterClient)) < 0)
               {
                  $this->handleError(ChatServer::SOCKET_ACCEPT_FAIL, '');
                  continue;
               }
               else
               {
                  array_push($this->clients, $client);
               }
            }
            else
            {
               $buffer = ''; // Wypełniany przez socket_recv (nieudokumentowany)
               $bytes = socket_recv($socket, $buffer, 2048, 0);
               if ($bytes == 0)
               {
                  $this->endSocket($socket);
               }
               else
               {
                  $allclients = $this->clients;
                  array_shift($allclients); // usuÅ serwer

                  $specialString = $buffer;

                  // Szukaj znacznika administratora
                  if (substr($specialString, 0, 7) == "[admin]")
                  {
                     $this->notifyClients($allclients, $socket, substr($buffer, 7), true);
                  }
                  else if (substr($specialString, 0, 4) == "koniec") // zamknij poÅÄczenie z klientem
                  {
                     $this->serverMessage("Zamykanie poÅÄczenia: " . $socket);
                     $this->notifyClient($socket, "Do widzenia");
                     $this->endSocket($socket);

                  }
                  else if (substr($specialString, 0, 4) == "help") // W odpowiedzi odeÅlij wiadomosÄ zawierajÄcÄ pomoc
                  {
                     $this->notifyClient($socket, "Pomoc programu Czat:\n" . "Aby wyjÅÄ z czata wpisz: exit");
                  }
                  else
                  {
                     $this->notifyClients($allclients, $socket, $buffer);
                  }
               }
            }

         }
      }
		
   }

   private function createMasterClient()
   {
      if (($this->masterClient = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) 
      {
         $this->handleError(ChatServer::SOCKET_CREATE_FAIL, $this->masterClient);
      } 

      socket_set_option($this->masterClient, SOL_SOCKET,SO_REUSEADDR, 1); 

      if (($bindRes = socket_bind($this->masterClient, $this->ipAddress, $this->port)) < 0) 
      {
         $this->handleError(ChatServer::SOCKET_BIND_FAIL, $bindRes);
      }
      //$bindRes = null;

      if (($ret = socket_listen($this->masterClient, 5)) < 0) 
      {
         $this->handleError(ChatServer::SOCKET_LISTEN_FAIL, $ret);
      }

      $this->clients = array($this->masterClient);

      $this->serverMessage("Serwer programu Czat zosta³ uchomiony");
   }

   private function notifyClient($sockID, $buffer)
   {
      foreach ($this->clients as $client)
      {	
         if ($client == $sockID)
         {
            socket_write($client, $buffer . $this->LINE_ENDING);
         }
      }
   }

   private function notifyClients($clients, $sock, $buffer, $admin=false)
   {
      foreach ($clients as $client)
      {
         if ($admin == true)
         {
            socket_write($client, "UWAGA ADMINISTRATORA: " . $buffer);
         }
         else
         {
            socket_write($client, "" . $sock . " napisał: " . $buffer);
         }
      }
   }

   private function handleError($str, $err)
   {
      if (ChatServer::SHOW_ERRORS)
      {
         $this->serverMessage(socket_strerror($str . ": powód: " . $err) . $this->LINE_ENDING);
      }
   }

   private function endSocket($sockID=null)
   {
      // ZakoÅcz dziaÅanie serwera, jeÅ¼eli nie utworzono gniazda
      if ($sockID == null)
      {
         $sockID = $this->masterClient;
      }

      $index = array_search($sockID, $this->clients);
      unset($this->clients[$index]);
      socket_close($sockID);
   }

   private function serverMessage($str)
   {
      print $str . $this->LINE_ENDING;
   }

}

?>
