From: direct Date: Fri, 13 Mar 2020 02:02:19 +0000 (+1000) Subject: new X-Git-Url: http://git.ultra-x.su/?a=commitdiff_plain;h=25c3ae2c41f59757d48466ccf217365122fa03af;p=dev new --- diff --git a/astapilib/TODO.html b/astapilib/TODO.html new file mode 100644 index 0000000..58f65eb --- /dev/null +++ b/astapilib/TODO.html @@ -0,0 +1,179 @@ +pbx*CLI> manager show commands + Action Synopsis + ------ -------- +- WaitEvent Wait for an event to occur. ++ DeviceStateList List the current known device states. ++ PresenceStateList List the current known presence states. ++ QueueReset Reset queue statistics. ++ QueueReload Reload a queue, queues, or any sub-section of a q ++ QueueRule Queue Rules. ++ QueueMemberRingInUse Set the ringinuse value for a queue member. ++ QueuePenalty Set the penalty for a queue member. ++ QueueLog Adds custom entry in queue_log. ++ QueuePause Makes a queue member temporarily unavailable. ++ QueueRemove Remove interface from queue. ++ QueueAdd Add interface to queue. ++ QueueSummary Show queue summary. ++ QueueStatus Show queue status. +- Queues Queues. ++ PlayDTMF Play DTMF signal on a specific channel. ++ StopMixMonitor Stop recording a call through MixMonitor, and fre ++ MixMonitor Record a call and mix the audio during the record ++ MixMonitorMute Mute / unMute a Mixmonitor recording. ++ VoicemailRefresh Tell Asterisk to poll mailboxes for a change ++ VoicemailUsersList List All Voicemail User Information. ++ ControlPlayback Control the playback of a file being played to a ++ MuteAudio Mute an audio stream. ++ DialplanExtensionRemove Remove an extension from the dialplan ++ DialplanExtensionAdd Add an extension to the dialplan ++ AgentLogoff Sets an agent as no longer logged in. ++ Agents Lists agents and their status. ++ ConfbridgeSetSingleVideoSrc Set a conference user as the single video source ++ ConfbridgeStopRecord Stop recording a Confbridge conference. ++ ConfbridgeStartRecord Start recording a Confbridge conference. ++ ConfbridgeLock Lock a Confbridge conference. ++ ConfbridgeUnlock Unlock a Confbridge conference. ++ ConfbridgeKick Kick a Confbridge user. ++ ConfbridgeUnmute Unmute a Confbridge user. ++ ConfbridgeMute Mute a Confbridge user. ++ ConfbridgeListRooms List active conferences. ++ ConfbridgeList List participants in a conference. ++ PRIDebugFileUnset Disables file output for PRI debug messages ++ PRIDebugFileSet Set the file used for PRI debug message output ++ PRIDebugSet Set PRI debug levels for a span ++ PRIShowSpans Show status of PRI spans. ++ DAHDIRestart Fully Restart DAHDI channels (terminates calls). ++ DAHDIShowChannels Show status of DAHDI channels. ++ DAHDIDNDoff Toggle DAHDI channel Do Not Disturb status OFF. ++ DAHDIDNDon Toggle DAHDI channel Do Not Disturb status ON. ++ DAHDIDialOffhook Dial over DAHDI channel while offhook. ++ DAHDIHangup Hangup DAHDI Channel. ++ DAHDITransfer Transfer DAHDI Channel. +- IAXregistry Show IAX registrations. +- IAXnetstats Show IAX Netstats. +- IAXpeerlist List IAX Peers. +- IAXpeers List IAX peers. ++ SIPpeerstatus Show the status of one or all of the sip peers. ++ SIPnotify Send a SIP notify. ++ SIPshowregistry Show SIP registrations (text format). ++ SIPqualifypeer Qualify SIP peers. ++ SIPshowpeer show SIP peer (text format). ++ SIPpeers List SIP peers (text format). ++ Park Park a channel. ++ ParkedCalls List parked calls. ++ Parkinglots Get a list of parking lots ++ FAXStats Responds with fax statistics ++ FAXSession Responds with a detailed description of a single ++ FAXSessions Lists active FAX sessions + AGI Add an AGI command to execute by Async AGI. ++ UnpauseMonitor Unpause monitoring of a channel. ++ PauseMonitor Pause monitoring of a channel. ++ ChangeMonitor Change monitoring filename of a channel. ++ StopMonitor Stop monitoring a channel. ++ Monitor Monitor a channel. ++ BridgeKick Kick a channel from a bridge. ++ BridgeDestroy Destroy a bridge. ++ BridgeInfo Get information about a bridge. ++ BridgeList Get a list of bridges in the system. ++ BlindTransfer Blind transfer channel(s) to the given destinatio +? Filter Dynamically add filters for the current manager s +? AOCMessage Generate an Advice of Charge message on a channel ++ ModuleCheck Check if module is loaded. ++ ModuleLoad Module management. ++ CoreShowChannels List currently active channels. ++ LoggerRotate Reload and rotate the Asterisk logger. ++ Reload Send a reload event. ++ CoreStatus Show PBX core status variables. ++ CoreSettings Show PBX core settings (version etc). ++ UserEvent Send an arbitrary event. ++ UpdateConfig Update basic configuration. ++ SendText Send text message to channel. ++ ListCommands List available manager commands. ++ MailboxCount Check Mailbox Message Count. ++ MailboxStatus Check mailbox. ++ AbsoluteTimeout Set absolute timeout. ++ PresenceState Check Presence State ++ ExtensionState Check Extension Status. ++ Command Execute Asterisk CLI Command. ++ Originate Originate a call. ++ Atxfer Attended transfer. ++ Redirect Redirect (transfer) a call. ++ ListCategories List categories in configuration file. ++ CreateConfig Creates an empty file in the configuration direct ++ Status List channel status. ++ GetConfigJSON Retrieve configuration (JSON format). ++ GetConfig Retrieve configuration. ++ Getvar Gets a channel variable or function value. ++ Setvar Sets a channel variable or function value. ++ ShowDialPlan Show dialplan contexts and extensions ++ Hangup Hangup channel. ++ Challenge Generate Challenge for MD5 Auth. ++ Login Login Manager. ++ Logoff Logoff Manager. ++ Events Control Event Flow. ++ Ping Keepalive command. ++ LocalOptimizeAway Optimize away a local channel when possible. ++ ExtensionStateList List the current known extension states. ++ MessageSend Send an out of call message to an endpoint. ++ Bridge Bridge two channels already in the PBX. ++ BridgeTechnologyUnsuspend Unsuspend a bridging technology. ++ BridgeTechnologySuspend Suspend a bridging technology. ++ BridgeTechnologyList List available bridging technologies and their st +- DataGet Retrieve the data api tree. ++ DBPut Put DB entry. ++ DBDelTree Delete DB Tree. ++ DBDel Delete DB entry. ++ DBGet Get DB Entry. + + + +pbx*CLI> agi show commands topic + Dead Command Description ++ No answer Answer channel + Yes asyncagi break Interrupts Async AGI ++ No channel status Returns status of the connected channel. ++ Yes database del Removes database key/value ++ Yes database deltree Removes database keytree/value ++ Yes database get Gets database value ++ Yes database put Adds/updates database value ++ Yes exec Executes a given Application ++ No get data Prompts for DTMF on a channel ++ Yes get full variable Evaluates a channel expression ++ No get option Stream file, prompt for DTMF, with timeout. ++ Yes get variable Gets a channel variable. ++ No hangup Hangup a channel. ++ Yes noop Does nothing. ++ No receive char Receives one character from channels supporting it. ++ No receive text Receives text from channels supporting it. ++ No record file Records to a given file. ++ No say alpha Says a given character string. ++ No say digits Says a given digit string. ++ No say number Says a given number. ++ No say phonetic Says a given character string with phonetics. ++ No say date Says a given date. ++ No say time Says a given time. ++ No say datetime Says a given time as specified by the format given. ++ No send image Sends images to channels supporting it. ++ No send text Sends text to channels supporting it. ++ No set autohangup Autohangup channel in some time. ++ No set callerid Sets callerid for the current channel. ++ No set context Sets channel context. ++ No set extension Changes channel extension. ++ No set music Enable/Disable Music on hold generator ++ No set priority Set channel dialplan priority. ++ Yes set variable Sets a channel variable. ++ No stream file Sends audio file on channel. ++ No control stream file Sends audio file on channel and allows the listener to control the stream. ++ No tdd mode Toggles TDD mode (for the deaf). ++ Yes verbose Logs a message to the asterisk verbose log. ++ No wait for digit Waits for a digit to be pressed. +- No speech create Creates a speech object. +- No speech set Sets a speech engine setting. +- Yes speech destroy Destroys a speech object. +- No speech load grammar Loads a grammar. +- Yes speech unload grammar Unloads a grammar. +- No speech activate grammar Activates a grammar. +- No speech deactivate grammar Deactivates a grammar. +- No speech recognize Recognizes speech. ++ No gosub Cause the channel to execute the specified dialplan subroutine. +pbx*CLI> agi show commands topic diff --git a/astapilib/agi.php b/astapilib/agi.php index c74de23..426f031 100644 --- a/astapilib/agi.php +++ b/astapilib/agi.php @@ -1,5 +1,5 @@ make_params(get_defined_vars()); + $ActId = $this->send_action('AGI', $params); + $response = $this->get_response($ActId); + LOG::log(__METHOD__.' '.$response['Message'], 5); + if ($response['Response'] == 'Success') + { + return TRUE; + } + else + { + return FALSE; + } + } + //Набор методов для работы со встроенной БД asterisk public function DBPut($Family, $Key, $Value=NULL) { diff --git a/astapilib/baseagi.php b/astapilib/baseagi.php index 5daa8ee..ca0a369 100644 --- a/astapilib/baseagi.php +++ b/astapilib/baseagi.php @@ -1,5 +1,5 @@ $val) { - if ($opt == 'autorefresh' and $val === TRUE) {new Timer(0.5, array(&$this,'refresh'), TRUE);} + if ($opt == 'autorefresh' and $val === TRUE) {new Timer(0.5, array(&$this,'refresh'), NULL, TRUE);} if ($opt == 'logverbose') {LOG::set_verbose($val);} - if ($opt == 'keepalive' and $val === TRUE) {new Timer(60, array(&$this,'ping'), TRUE);} + if ($opt == 'keepalive' and $val === TRUE) {new Timer(60, array(&$this,'ping'), NULL, TRUE);} } } @@ -186,7 +186,7 @@ class baseAMI return FALSE; } } - + //подписка на события ami public function enable_events($toggle = FALSE) diff --git a/astapilib/timer.php b/astapilib/timer.php new file mode 100644 index 0000000..dc577cd --- /dev/null +++ b/astapilib/timer.php @@ -0,0 +1,119 @@ +id = uniqid(); + $this->time = $time; + $this->UserCall = $callable; + $this->UserCallArg = $args; + $this->type = $type; + if (!is_callable($this->UserCall, TRUE, $callablename)) + { + throw new Exception("parameter 2 must be callable, \$callable=". $callablename); + } + else + { + $this->start(); + self::$timers[$this->id] = &$this; + } + } + + public function __destruct() + { + unset(self::$timers[$this->id]); + } + + private function getId() + { + return $this->id; + } + + public function getTimeLeft() + { + return $this->ltime; + } + + public function setTimeLeft($time) + { + $this->time = $time; + } + + + public function reset() + { + $this->StartTime = microtime(true); + $this->IsStop = FALSE; + } + + public function start() + { + $this->reset(); + $this->IsStop = FALSE; + } + + public function stop() + { + $this->IsStop = TRUE; + } + + public static function poll() + { + foreach (self::$timers as $timer) + { + if ($timer->IsStop == FALSE) + { + $timer->update(); + } + } + } + + private function update() + { + $this->ltime = $this->StartTime - microtime(true) + $this->time; + if ($this->ltime <= 0) + { + $this->ltime = 0; + $this->task(); + if ($this->type === FALSE) + { + $this->stop(); + } + elseif ($this->type === TRUE) + { + $this->reset(); + } + elseif ($this->type === NULL) + { + $this->stop(); + $this->__destruct(); + } + } + } + + private function task() + { + call_user_func_array ($this->UserCall, $this->UserCallArg); + } +} diff --git a/call.php b/call.php new file mode 100644 index 0000000..6eaa22a --- /dev/null +++ b/call.php @@ -0,0 +1,296 @@ +#!/usr/bin/env php +get_variable('WAAN',true); +$NO_PROGRESS = $AGI->get_variable('NO_PROGRESS',true); +$NO_LINEUPDATE = $AGI->get_variable('NO_LINEUPDATE',true); +$IS_BLACKLISTED = $AGI->database_get('SET/'.$AGI->request['agi_extension'].'/BL',$AGI->request['agi_callerid']); +if ($IS_BLACKLISTED['data'] == 1) +{ + $AGI->verbose('The CID '.$AGI->request['agi_callerid'].' for extension '.$AGI->request['agi_extension'].' is blacklisted.'); + if ($NO_PROGRESS == 'YES') + { + $AGI->exec('Answer'); + } + else + { + $AGI->exec('Proceeding'); + } + sleep(1); + $AGI->exec('Playback','cid_blacklisted,noanswer'); + $AGI->exec('Hangup',54); + +} + +$is_busy = false; +$CIDCP = ''; +//пишем все вызовы +$UID = $AGI->get_variable('UNIQUEID',true); +$MONFILENAME = date('Y').'/'.date('m').'/'.date('d').'/'.$UID; +$AGI->set_variable('CDR(moni_file)',$MONFILENAME); +//$AGI->set_variable('MONITOR_EXEC','/opt/monproc $1 $2 $3'); + +$AGI->exec('Monitor','wav,'.$MONFILENAME.',mb'); +//канал или каналы, которые вызываются (обязательный) +if (isset($AGI->request['agi_arg_1'])) + { + $dialchans = $AGI->request['agi_arg_1']; + //создание в astDB автоматически создаваемого hint + $AGI->database_put('hints',$AGI->request['agi_extension'],$AGI->request['agi_arg_1']); + } +else + { + $AGI->verbose('Missing 1 pagameter'); + } +//параметры для Dial() +if (isset($AGI->request['agi_arg_2'])) + { + $opts = $AGI->request['agi_arg_2']; + } +else + { + $opts = ''; + } +if ($NO_LINEUPDATE == 'YES') +{ + $opts .= 'I'; +} + +//метод вызова нескольких каналов +if (isset($AGI->request['agi_arg_3'])) + { + $mode = $AGI->request['agi_arg_3']; + } +else + { + $mode = 'group'; + } +//ограничение колличества звонков на extension +if (isset($AGI->request['agi_arg_4'])) + { + $_extenlimit = $AGI->request['agi_arg_4']; + } + +//проверка callerid name на пустоту +if ($AGI->request['agi_calleridname'] == '') +{ + $AGI->set_variable('CALLERID(name)',$AGI->request['agi_callerid']); +} + +//кодировка callerid name +//определение колличества пиров для вызова и ограничителей вызова +$peers = explode('&',$dialchans); +//когда пир один +if (count($peers) == 1) + { + $is_single_peer = true; + $dialchan_arr = explode('/',$dialchans); + $_busylevel = $AGI->get_variable('SIPPEER('.$dialchan_arr[1].',busylevel)',true); + $_curcalls = $AGI->get_variable('SIPPEER('.$dialchan_arr[1].',curcalls)',true); + if ($_curcalls >= $_busylevel and $_busylevel != 0) {$is_busy = true;} + $CIDCP = $AGI->get_variable('SIPPEER('.$dialchan_arr[1].',chanvar[CIDNAMECP])',true); + } +//когда пир не один +else + { + + $is_single_peer = false; + } +//если есть ограничения на extension +if (isset($_extenlimit)) +{ + $_cur_exten_calls = $AGI->get_variable('GROUP_COUNT('.$AGI->request['agi_extension'].'@extenlimit)',true); + if ($_cur_exten_calls >= $_extenlimit) + { + $is_busy = true; + } + else + { + $AGI->set_variable('GROUP(extenlimit)',$AGI->request['agi_extension']); + } +} + +if ($CIDCP == ''){$CIDCP = 'lat';} +//преобразование кодировки +switch ($CIDCP) + { + case 'lat': + $AGI->set_variable('CALLERID(name)',mb_transliterate($AGI->request['agi_calleridname'])); + break; + case 'cp1251': + $AGI->set_variable('CALLERID(name)',mb_convert_encoding($AGI->request['agi_calleridname'], $CIDCP['data'], 'UTF-8')); + break; + } + + + +//dial process +//посылать или не посылать progress + +if ($NO_PROGRESS != 'YES') + { + $AGI->exec('Proceeding'); + $AGI->exec('Playtones',('!0/600,!1000/50')); + sleep(1); + } + //если пир один то позволять переадресации +if ($is_single_peer) + { + //подготовка данных в случае переадресации + $dialchan_arr = explode('/',$dialchans); + $peername = $dialchan_arr[1]; + $peercontext = $AGI->get_variable('SIPPEER('.$peername.',context)',true); + $db_query = 'SET/'.$peername.'/CF'; + //get settings for extension + + $CF = $AGI->database_get($db_query,'mode'); + if($CF['result'] == 1) + { + if ($CF['data'] == '1') + { + $AGI->set_variable('REDIRECTING(from-num)',$peername); + $AGI->set_variable('REDIRECTING(reason)','4'); + $FWDNUM = $AGI->database_get($db_query,'uncond'); + $AGI->exec_goto($peercontext,$FWDNUM['data'],'1'); + die(); + } + if ($CF['data'] == '2') + { + $FWDNUM = $AGI->database_get($db_query,'unansw'); + $timeout = $AGI->database_get($db_query,'timeout'); + $timeout = $timeout['data']; + if ($timeout < 1) + { + $timeout = '15'; + } + } + if ($CF['data'] == '3') + { + $FWDNUM = $AGI->database_get($db_query,'busy'); + } + } + } +else + { + //для груповых вызовов не позволять переадресацию во избежание неадекватного поведения + $CF['data'] = '0'; + } + +//одиночный вызов +if ($is_single_peer and !$is_busy) + { + $AGI->exec('Dial',$dialchans.','.$timeout.',TtkKg'.$opts); + } +//групповой вызов +if ($mode == 'group' and !$is_single_peer) + { + if (!$is_busy) + { + $AGI->exec('Dial',$dialchans.','.$timeout.',iTtkK'.$opts); + $AGI->verbose("CAUSE:".$AGI->get_variable('HANGUPCAUSE',true)); + } + } + +//последовательный вызов +if ($mode == 'serial' and !$is_single_peer) + { + foreach ($peers as $val) + { + $AGI->exec('Dial',$val.','.$timeout.',iTtkKg'.$opts); + $HANGUPCAUSE = $AGI->get_variable('HANGUPCAUSE',true); + if ($HANGUPCAUSE == 16 or $HANGUPCAUSE == 0 or $HANGUPCAUSE == 18){break;} + } + } + +//получение статуса завершения +$HANGUPCAUSE = $AGI->get_variable('HANGUPCAUSE',true); +$DIALSTATUS = $AGI->get_variable('DIALSTATUS',true); + +if ($is_busy){$HANGUPCAUSE = '17';} +if ($DIALSTATUS == 'NOANSWER'){$HANGUPCAUSE = '18';} + +switch ($HANGUPCAUSE) +{ + case "20": + if ($CF['data'] == '2') + { + $AGI->set_variable('REDIRECTING(reason)','3'); + $AGI->set_variable('REDIRECTING(from-num)',$peername); + $AGI->exec('Playback','pereadresacija-zvonka&ozhidajte-soedinenija,noanswer'); + $AGI->exec_goto($peercontext,$FWDNUM['data'],'1'); + exit(); + } + else + { + for ($i=0;$i<2;$i++) + { + $AGI->exec('Playtones','info'); + sleep(2); + $AGI->exec('Playback','abonent-not-connected,noanswer'); + sleep(2); + } + sleep(1); + $AGI->exec('Hangup',$HANGUPCAUSE); + exit(); + } + break; + + case "18": + if ($CF['data'] == '2') + { + $AGI->set_variable('REDIRECTING(reason)','3'); + $AGI->set_variable('REDIRECTING(from-num)',$peername); + //$AGI->exec('Playback','pereadresacija-zvonka&ozhidajte-soedinenija,noanswer'); + $AGI->exec_goto($peercontext,$FWDNUM['data'],'1'); + die(); + } + else + { + $AGI->exec('Playback','abonent-noanswer,noanswer'); + $AGI->exec('Hangup',$HANGUPCAUSE); + } + break; + + case "17": + if ($CF['data'] == '3') + { + $AGI->set_variable('REDIRECTING(reason)','1'); + $AGI->set_variable('REDIRECTING(from-num)',$peername); + $AGI->exec_goto($peercontext,$FWDNUM['data'],'1'); + exit(); + } + else + { + $AGI->exec('Playback','number&vm-isonphone,noanswer'); + $AGI->exec('Hangup',$HANGUPCAUSE); + + } + break; + + case "0": + case "16": + $AGI->verbose("Normal call clearing, CAUSECODE: ".$HANGUPCAUSE); + break; + + default: + $AGI->verbose("CAUSECODE not handled, value: ".$HANGUPCAUSE); + break; +} + + +?> diff --git a/check_code.php b/check_code.php new file mode 100644 index 0000000..17b7393 --- /dev/null +++ b/check_code.php @@ -0,0 +1,33 @@ +\n"; +require_once 'astapilib/ami.php'; + +var_dump($_GET); +if (!isset($_GET['phone'])) +{ + exit(); +} +if(preg_match('/^7\d{10}$/', $_GET['phone']) != 1) +{ + //exit(); +} + +$AMI = new AMI(array('autorefresh' => TRUE, 'logverbose' => 6)); +$is_connected = $AMI->connect("127.0.0.1", "monast", "blabla"); +$AMI->add_event_handler('originateresponse', 'waitanswer'); +$AMI->enable_events(TRUE); + +if (!$is_connected) +{ + exit(); +} + +var_dump($AMI->Originate("Local/{$_GET['phone']}@c-2", NULL, NULL, NULL, 'AGI', 'agi:async', 30, 3500, NULL, NULL, NULL, NULL, 'azaza', 'ololo')); + +sleep(5); +$AMI->AGI("Local/{$_GET['phone']}@c-2", 'NoOp'); \ No newline at end of file diff --git a/shmtester.php b/shmtester.php new file mode 100644 index 0000000..724f8fe --- /dev/null +++ b/shmtester.php @@ -0,0 +1,32 @@ +#!/usr/bin/env php +abc)); +//$MEM->abc = 2; +//$MEM->bsd = 'asasa'; + +//var_dump(isset($MEM->abc)); +//var_dump($MEM->SearchFreeTOCSlot()); +var_dump($MEM->ReadTOCSlot(0)); +var_dump(isset($MEM->somevar)); +$MEM->somevar = 'aaaaaaaa'; +$MEM->var2 = 'bbbbbbbb'; +$MEM->var3 = 'cccccccc'; +$MEM->var4 = 'dddddddd'; + +var_dump($MEM->ReadTOCSlot(0)); + +//var_dump($MEM->ReadTOCSlot(1)); +//var_dump($MEM->ReadTOCSlot(2)); +//var_dump($MEM->ReadTOCSlot(3)); + + + +$MEM->printshm(); \ No newline at end of file diff --git a/shmvar.php b/shmvar.php new file mode 100644 index 0000000..1bb53c8 --- /dev/null +++ b/shmvar.php @@ -0,0 +1,369 @@ +shmkey = ftok(__FILE__, 'a'); + } + else + { + $this->shmkey = $shmkey; + } + $this->max_toc_slots = $maxvars; + $this->datasize = $datasize; + $this->dataoffset = $maxvars * TOCBLOCKSIZE; + $this->namecache = []; + $pagesize = $maxvars * TOCBLOCKSIZE + $datasize; + //открытие страницы памяти + $this->shm = @shmop_open($this->shmkey, 'w', 0, 0); + if (!$this->shm) + { + $this->shm = @shmop_open($this->shmkey, 'c', 0640, $pagesize); + } +// // объявление свободного пространства +// $this->WriteTOCSlot(0, '', 0xFF, 0x00, 0, $datasize, 0xFFAAFFAA); +// $this->WriteHeaderSegment(0, 0xFFFFFFFF, $datasize); + + } + public function __isset ($name) + { + if ($this->SearchVarByName($name)) + { + return TRUE; + } + else + { + return FALSE; + } + } + + public function __set ($name , $value) + { + $toc_slot = $this->SearchVarByName($name); + if ($toc_slot === FALSE) + { + $toc_slot = $this->SearchFreeTOCSlot(); + } + + switch (gettype($value)) + { + case "NULL": + $type = 0; + break; + case "boolean": + $type = 1; + break; + case "integer": + $type = 2; + break; + case "double": + $type = 3; + break; + case "string": + $type = 4; + break; + case "array": + $type = 5; + break; + case "object": + $type = 6; + break; + case "resource": + $type = 7; + break; + case "resource (closed)": + $type = 8; + break; + case "unknown type": + $type = 9; + break; + default: + $type = 10; + break; + } + $size = strlen($value); + $segments = $this->ReserveDataSegments($size); + var_dump($segments); + $lastdatapos = 0; + foreach ($segments as $segment) + { + $segmentsize = $segment['e'] - $segment['s']; + $dataforwrite = substr($value, $lastdatapos, $segmentsize); + $lastdatapos = $lastdatapos + $segmentsize; + $this->WriteDataSegment($segment['s'], $value); + } + $flags = 0; + $startaddr = 0; + $nexttoc_slot = 0xAAAAAAAA; + + $data = $value; + $this->WriteTOCSlot($toc_slot, $name, 0xFF, $flags, $startaddr, $size, $nexttoc_slot); + } + +//public mixed __get ( string $name ) + //public bool __isset ( string $name ) + //public void __unset ( string $name ) + public function ReserveDataSegments($size) + { + $segments = $this->SearchFreeSegments($size); + $totalsize = 0; + foreach ($segments as $segment => $address) + { + $totalsize = $totalsize + ($address['e'] - $address['s'] - 8); + } + + $lastsegmentsize = ($address['e'] - $address['s']); + $newlastsegmentsize = $lastsegmentsize - ($totalsize - $size); + $newlastsegmentaddress = $address['e'] - $newlastsegmentsize; + $segments[$segment]['e'] = $segments[$segment]['s'] + $newlastsegmentsize; + $newfreesegmentaddress = $segments[$segment]['e'] + 1; + $newfreesegmentsize = ($lastsegmentsize - $newlastsegmentsize); + $TocSlot0 = $this->ReadTOCSlot(0); + $LastSegmentHeader = $this->ReadDataSegment($segments[$segment]['s'], 0); + $this->WriteTOCSlot(0, '', 0xFF, 0x00, $newfreesegmentaddress, $TocSlot0['size'] - $size, 0); + $this->WriteHeaderSegment($newfreesegmentaddress, $LastSegmentHeader['nextaddress'], $newfreesegmentsize); + $this->WriteHeaderSegment($segments[$segment]['s'], 0xFFFFFFFF, $newlastsegmentsize); + return $segments; + } + + public function FreeDataSegments($size) + { + + } + + public function SearchFreeTOCSlot() + { + for ($TocSlotId = 0; $TocSlotId < $this->max_toc_slots; $TocSlotId++) + { + $data = $this->ReadTOCSlot($TocSlotId); + if($data['name'] == '' && $data['type'] == 0) + { + return $TocSlotId; + } + } + return FALSE; + } + + public function SearchFreeSegments($size) + { + for ($TocSlotId = 0; $TocSlotId < $this->max_toc_slots; $TocSlotId++) + { + $data = $this->ReadTOCSlot($TocSlotId); + if($data['type'] != 0) + { + $usedsegments[] = $data['address']; + $usedsegments[] = $data['address'] + $data['size']; + + } + } + sort($usedsegments, SORT_NUMERIC); + foreach ($usedsegments as $key => $val) + { + if ($key % 2 == 0) + { + $tmp1['s'] = $val; + } + else + { + $tmp1['e'] = $val; + $tmp2[] = $tmp1; + } + $usedsegments = $tmp2; + unset($tmp1,$tmp2); + } + $addrpointer = 0; + $counter = 0; + foreach ($usedsegments as $segment) + { + if($segment['s'] == 0) + { + $addrpointer = $segment['e'] + 1; + continue; + } + if ($segment['s'] == $addrpointer) + { + $addrpointer = $segment['e'] + 1; + } + if ($segment['s'] != $addrpointer) + { + $freesegment['s'] = $addrpointer; + $freesegment['e'] = $segment['s'] - 1; + } + $freesegments[] = $freesegment; + } + + $start - 0; + $stop = $this->datasize; + + + $freepointer = $this->ReadTOCSlot(0); + if ($size > $freepointer['size']) + { + return FALSE; + } + $segaddr = $freepointer['address']; + $DiscoveredSpaseSize = 0; + while ($segaddr != 0xFFFFFFFF) + { + $segheader = $this->ReadDataSegment($segaddr, 0); + $freesegment['s'] = $segaddr; + $freesegment['e'] = $segaddr + $segheader['size']; + $freesegments[] = $freesegment; + $segaddr = $segheader['nextaddress']; + $DiscoveredSpaseSize = $DiscoveredSpaseSize + ($freesegment['e'] - $freesegment['s'] - 8); + if ($DiscoveredSpaseSize >= $size) + { + break; + } + } + + return $freesegments; + } + + protected function SearchVarByName($name) + { + for ($TocSlotId = 0; $TocSlotId < $this->max_toc_slots; $TocSlotId++) + { + $data = $this->ReadTOCSlot($TocSlotId); + if($data['name'] == $name && $data['type'] != 0) + { + return $TocSlotId; + } + } + return FALSE; + } + + /* modes: + * 0 - only header + * 1 - one page data + * 2 - one page data + header as named array + * 3 - autoassemble all data pages (default) + */ + protected function ReadDataSegment($address, $mode = 3) + { + $address = $this->dataoffset + $address; + $header = shmop_read ($this->shm, $address, 8); + $header = unpack('Nnextaddress/Nsize', $header); + switch ($mode) + { + case 0: + return $header; + case 1: + return shmop_read($this->shm, $address + 8 , $header['size']); + case 2: + $header['data'] = shmop_read($this->shm, $address + 8 , $header['size']); + return $header; + } + $data = ''; + while (TRUE); + { + $data .= shmop_read($this->shm, $address + 8 , $header['size']); + if ($header['nextaddress'] == 0xFFFFFFFF) + { + return $data; + } + $header = shmop_read ($this->shm, $this->dataoffset + $header['nextaddress'], 8); + $header = unpack('Nnextaddress/Nsize', $header); + $address = $this->dataoffset + $header['nextaddress']; + } + + } + + protected function WriteDataSegment($address, $data) + { + $address = $this->dataoffset + $address; + $nextaddress = 0xFFFFFFFF; + $size = strlen($data); +// shmop_write ($this->shm , pack('NNC*', $nextaddress, $size) , $address); + shmop_write ($this->shm , pack('NNa*', $nextaddress, $size, $data) , $address); + } + + protected function WriteHeaderSegment($address, $nextaddress, $size) + { + $address = $this->dataoffset + $address; + shmop_write ($this->shm , pack('NN', $nextaddress, $size) , $address); + } + + protected function WriteTOCSlot($TocSlotId, $name, $type, $lock, $startaddr, $size, $nexttoc_slot) + { + $recaddr = $TocSlotId * TOCBLOCKSIZE; + $block = pack('a127CCNNN', $name, $type, $lock, $startaddr, $size, $nexttoc_slot); + shmop_write ($this->shm , $block , $recaddr); + + } + public function ReadTOCSlot($TocSlotId) + { + // 1-127 var name + // 128 type + // 129 lock flag + // 130-133 address + // 134-137 size + // 138-141 next nexttoc_slot + $recaddr = $TocSlotId * TOCBLOCKSIZE; + $block = shmop_read ($this->shm, $recaddr, TOCBLOCKSIZE); +// $this->debug($block); + $result = unpack('a127name/Ctype/Cflags/Naddress/Nsize/Nnexttocslot', $block); + return $result; + } + + public function printshm () + { + $this->debug(shmop_read ($this->shm, 0, shmop_size($this->shm))); + } + + public function debug($data) + { + $str = ''; + $CPS = 16; + $pc = 0; + $maxaddrlen = strlen(dechex(strlen($data))); + echo str_repeat(" ", $maxaddrlen+2); + for ($i = 0; $i < $CPS; $i++) + { + echo sprintf("%'02X", $i)," "; + } + + for ($i = 0; $i < strlen($data); $i++) + { + $pc++; + if(($i % $CPS) == 0) + { + echo "\t",$str, PHP_EOL, sprintf("%'0{$maxaddrlen}X", $i)," "; + $str = ''; + $pc = 0; + } + $byte = ord($data{$i}); + echo sprintf("%'02X", $byte)," "; + if($byte > 31 && $byte < 127 ) + { + $str .= chr($byte); + } + else + { + $str .= "."; + } + } + echo str_repeat(" ", ($CPS-$pc)-1), "\t", $str, PHP_EOL; + } +} \ No newline at end of file