В сети распространено заблуждение, что дружба PHP и Asterisk CLI — это костыль. Возможно, это и так, но иногда по требованию заказчика для интеграции, например, с CRM системой приходится связывать с VOIP, и чаще всего это Asterisk.
Как же быстро и просто подвязать несколько команд из CLI, да так, чтобы можно было получать уже готовые массивы данных для передачи в веб-приложение?
На самом деле все очень просто. Немного рутиной работы, немного логики и, вуаля.
Для получения данных мы будем использовать документированную возможность Asterisk обращение к CLI из-вне.
Читаем матчасть:
-x: В комбинации с параметром -r, выполняет команду CLI Asterisk
Пример: asterisk -rx «sip reload» — произведет рестарт sip.
-n: Запретить поддержку цветного вывода ANSI
Но как я не пытался отключить цветной вывод, это у меня не получилось, видать руки кривоватые.
Для запуска команды мы будем использовать две функции PHP, exec() и passthru().
<?php
function asterisk_cli_exec ($a)
{
exec("asterisk -rx '$a'", $b);
return $b;
}
function asterisk_cli_passthru ($a)
{
passthru("asterisk -rx '$a'", $b);
return $b;
}
?>
[0]=>
string(63) «manager reload — Reload manager configurations»
[1]=>
string(85) «manager set debug [on|off] — Show, enable, disable debugging of the manager code»
[2]=>
string(66) «manager show command — Show a manager interface command»
[3]=>
string(65) «manager show commands — List manager interface commands»
[4]=>
string(72) «manager show connected — List connected manager interface users»
[5]=>
string(70) «manager show eventq — List manager interface queued events»
[6]=>
string(63) «manager show events — List manager interface events»
[7]=>
string(64) «manager show event — Show a manager interface event»
[8]=>
string(62) «manager show settings — Show manager global settings»
[9]=>
string(63) «manager show users — List configured manager users»
[10]=>
string(80) «manager show user — Display information on a specific manager user»
}
manager set debug [on|off] — Show, enable, disable debugging of the manager code
manager show command — Show a manager interface command
manager show commands — List manager interface commands
manager show connected — List connected manager interface users
manager show eventq — List manager interface queued events
manager show events — List manager interface events
manager show event — Show a manager interface event
manager show settings — Show manager global settings
manager show users — List configured manager users
manager show user — Display information on a specific manager user
В итоге мы получили две функции, которые дают нам 2 различных варианта для выдачи информации:
exec() — в виде массива строк;
passthru() — прямой вывод результата.
Функция asterisk_cli_passthru() может пригодиться только для создания собственного CLI интерфейса из браузера. В основном прямой вывод не требуется. Для web приложений нужны массивы. На данном этапе мы сразу же забудем про эту «вкусняшку», так как она уже полностью реализована, и более для нее ничего не требуется.
Итак перейдем к самому интересному.
Следующим шагом мы разделим команды на «простые» и «сложные». Простые команды это те которые в своем выводе не выдают ассоциативный массив данных.
}
[0]=>
string(20) «manager debug is off»
}
[Syntax]
Action: WaitEvent
[ActionID:] <value>
Timeout: <value>
[Synopsis]
Wait for an event to occur.
[Description]
This action will ellicit a 'Success' response. Whenever a manager event is
queued. Once WaitEvent has been called on an HTTP manager session, events
will be generated and queued.
[Arguments]
ActionID
ActionID for this transaction. Will be returned.
Timeout
Maximum time (in seconds) to wait for events, '-1' means forever
Event: Status
[Synopsis]
Raised in response to a Status command.
[Syntax]
Event: Status
[ActionID:] <value>
Type: <value>
DNID: <value>
TimeToHangup: <value>
BridgeID: <value>
Linkedid: <value>
Application: <value>
Data: <value>
Nativeformats: <value>
Readformat: <value>
Readtrans: <value>
Writeformat: <value>
Writetrans: <value>
Callgroup: <value>
Pickupgroup: <value>
Seconds: <value>
array(137) {
[0]=>
string(42) " Action Synopsis"
[1]=>
string(42) " ------ --------"
[2]=>
string(61) " WaitEvent Wait for an event to occur."
[3]=>
string(71) " DeviceStateList List the current known device states."
[4]=>
string(73) " PresenceStateList List the current known presence states."
[5]=>
string(57) " QueueReset Reset queue statistics."
[6]=>
string(79) " QueueReload Reload a queue, queues, or any sub-section of"
[7]=>
string(46) " QueueRule Queue Rules."
[8]=>
string(77) " QueueMemberRingInUse Set the ringinuse value for a queue member."
[9]=>
string(69) " QueuePenalty Set the penalty for a queue member."
[10]=>
string(65) " QueueLog Adds custom entry in queue_log."
[11]=>
string(79) " QueuePause Makes a queue member temporarily unavailable."
[12]=>
string(62) " QueueRemove Remove interface from queue."
[13]=>
string(57) " QueueAdd Add interface to queue."
[14]=>
string(53) " QueueSummary Show queue summary."
[15]=>
string(52) " QueueStatus Show queue status."
[16]=>
string(41) " Queues Queues."
[17]=>
string(80) " ControlPlayback Control the playback of a file being played to"
[18]=>
string(79) " StopMixMonitor Stop recording a call through MixMonitor, and"
[19]=>
string(80) " MixMonitor Record a call and mix the audio during the rec"
[20]=>
string(71) " MixMonitorMute Mute / unMute a Mixmonitor recording."
[21]=>
string(78) " VoicemailRefresh Tell Asterisk to poll mailboxes for a change"
[22]=>
string(70) " VoicemailUsersList List All Voicemail User Information."
[23]=>
string(73) " PlayDTMF Play DTMF signal on a specific channel."
[24]=>
string(55) " MuteAudio Mute an audio stream."
[25]=>
string(80) " ConfbridgeSetSingleVideoSrc Set a conference user as the single video sour"
[26]=>
string(73) " ConfbridgeStopRecord Stop recording a Confbridge conference."
[27]=>
string(74) " ConfbridgeStartRecord Start recording a Confbridge conference."
[28]=>
string(63) " ConfbridgeLock Lock a Confbridge conference."
[29]=>
string(65) " ConfbridgeUnlock Unlock a Confbridge conference."
[30]=>
string(57) " ConfbridgeKick Kick a Confbridge user."
[31]=>
string(59) " ConfbridgeUnmute Unmute a Confbridge user."
[32]=>
string(57) " ConfbridgeMute Mute a Confbridge user."
[33]=>
string(58) " ConfbridgeListRooms List active conferences."
[34]=>
string(68) " ConfbridgeList List participants in a conference."
[35]=>
string(58) " MeetmeListRooms List active conferences."
[36]=>
string(68) " MeetmeList List participants in a conference."
[37]=>
string(55) " MeetmeUnmute Unmute a Meetme user."
[38]=>
string(53) " MeetmeMute Mute a Meetme user."
[39]=>
string(80) " PJSIPNotify Send a NOTIFY to either an endpoint or an arbi"
[40]=>
string(69) " PJSIPShowRegistrationsOutbound Lists PJSIP outbound registrations."
[41]=>
string(70) " PJSIPUnregister Unregister an outbound registration."
[42]=>
string(68) " PJSIPShowRegistrationsInbound Lists PJSIP inbound registrations."
[43]=>
string(77) " PRIDebugFileUnset Disables file output for PRI debug messages"
[44]=>
string(80) " PRIDebugFileSet Set the file used for PRI debug message output"
[45]=>
string(65) " PRIDebugSet Set PRI debug levels for a span"
[46]=>
string(59) " PRIShowSpans Show status of PRI spans."
[47]=>
string(80) " DAHDIRestart Fully Restart DAHDI channels (terminates calls"
[48]=>
string(64) " DAHDIShowChannels Show status of DAHDI channels."
[49]=>
string(80) " DAHDIDNDoff Toggle DAHDI channel Do Not Disturb status OFF"
[50]=>
string(80) " DAHDIDNDon Toggle DAHDI channel Do Not Disturb status ON."
[51]=>
string(72) " DAHDIDialOffhook Dial over DAHDI channel while offhook."
[52]=>
string(55) " DAHDIHangup Hangup DAHDI Channel."
[53]=>
string(57) " DAHDITransfer Transfer DAHDI Channel."
[54]=>
string(80) " SIPpeerstatus Show the status of one or all of the sip peers"
[55]=>
string(52) " SIPnotify Send a SIP notify."
[56]=>
string(71) " SIPshowregistry Show SIP registrations (text format)."
[57]=>
string(52) " SIPqualifypeer Qualify SIP peers."
[58]=>
string(62) " SIPshowpeer show SIP peer (text format)."
[59]=>
string(63) " SIPpeers List SIP peers (text format)."
[60]=>
string(57) " IAXregistry Show IAX registrations."
[61]=>
string(52) " IAXnetstats Show IAX Netstats."
[62]=>
string(49) " IAXpeerlist List IAX Peers."
[63]=>
string(49) " IAXpeers List IAX peers."
[64]=>
string(49) " Park Park a channel."
[65]=>
string(52) " ParkedCalls List parked calls."
[66]=>
string(60) " Parkinglots Get a list of parking lots"
[67]=>
string(77) " AGI Add an AGI command to execute by Async AGI."
[68]=>
string(62) " FAXStats Responds with fax statistics"
[69]=>
string(80) " FAXSession Responds with a detailed description of a sing"
[70]=>
string(59) " FAXSessions Lists active FAX sessions"
[71]=>
string(80) " PJSIPShowResourceLists Displays settings for configured resource list"
[72]=>
string(54) " PJSIPShowSubscriptionsOutbound Lists subscriptions."
[73]=>
string(54) " PJSIPShowSubscriptionsInbound Lists subscriptions."
[74]=>
string(66) " UnpauseMonitor Unpause monitoring of a channel."
[75]=>
string(64) " PauseMonitor Pause monitoring of a channel."
[76]=>
string(74) " ChangeMonitor Change monitoring filename of a channel."
[77]=>
string(60) " StopMonitor Stop monitoring a channel."
[78]=>
string(52) " Monitor Monitor a channel."
[79]=>
string(64) " PJSIPQualify Qualify a chan_pjsip endpoint."
[80]=>
string(80) " PJSIPShowEndpoint Detail listing of an endpoint and its objects."
[81]=>
string(56) " PJSIPShowEndpoints Lists PJSIP endpoints."
[82]=>
string(63) " BridgeKick Kick a channel from a bridge."
[83]=>
string(51) " BridgeDestroy Destroy a bridge."
[84]=>
string(65) " BridgeInfo Get information about a bridge."
[85]=>
string(70) " BridgeList Get a list of bridges in the system."
[86]=>
string(80) " BlindTransfer Blind transfer channel(s) to the given destina"
[87]=>
string(80) " Filter Dynamically add filters for the current manage"
[88]=>
string(80) " AOCMessage Generate an Advice of Charge message on a chan"
[89]=>
string(60) " ModuleCheck Check if module is loaded."
[90]=>
string(52) " ModuleLoad Module management."
[91]=>
string(65) " CoreShowChannels List currently active channels."
[92]=>
string(72) " LoggerRotate Reload and rotate the Asterisk logger."
[93]=>
string(54) " Reload Send a reload event."
[94]=>
string(65) " CoreStatus Show PBX core status variables."
[95]=>
string(71) " CoreSettings Show PBX core settings (version etc)."
[96]=>
string(58) " UserEvent Send an arbitrary event."
[97]=>
string(61) " UpdateConfig Update basic configuration."
[98]=>
string(63) " SendText Send text message to channel."
[99]=>
string(66) " ListCommands List available manager commands."
[100]=>
string(62) " MailboxCount Check Mailbox Message Count."
[101]=>
string(48) " MailboxStatus Check mailbox."
[102]=>
string(55) " AbsoluteTimeout Set absolute timeout."
[103]=>
string(54) " PresenceState Check Presence State"
[104]=>
string(57) " ExtensionState Check Extension Status."
[105]=>
string(63) " Command Execute Asterisk CLI Command."
[106]=>
string(51) " Originate Originate a call."
[107]=>
string(52) " Atxfer Attended transfer."
[108]=>
string(61) " Redirect Redirect (transfer) a call."
[109]=>
string(72) " ListCategories List categories in configuration file."
[110]=>
string(80) " CreateConfig Creates an empty file in the configuration dir"
[111]=>
string(54) " Status List channel status."
[112]=>
string(71) " GetConfigJSON Retrieve configuration (JSON format)."
[113]=>
string(57) " GetConfig Retrieve configuration."
[114]=>
string(76) " Getvar Gets a channel variable or function value."
[115]=>
string(76) " Setvar Sets a channel variable or function value."
[116]=>
string(71) " ShowDialPlan Show dialplan contexts and extensions"
[117]=>
string(49) " Hangup Hangup channel."
[118]=>
string(66) " Challenge Generate Challenge for MD5 Auth."
[119]=>
string(48) " Login Login Manager."
[120]=>
string(49) " Logoff Logoff Manager."
[121]=>
string(53) " Events Control Event Flow."
[122]=>
string(52) " Ping Keepalive command."
[123]=>
string(78) " LocalOptimizeAway Optimize away a local channel when possible."
[124]=>
string(74) " ExtensionStateList List the current known extension states."
[125]=>
string(77) " MessageSend Send an out of call message to an endpoint."
[126]=>
string(73) " Bridge Bridge two channels already in the PBX."
[127]=>
string(71) " DialplanExtensionRemove Remove an extension from the dialplan"
[128]=>
string(66) " DialplanExtensionAdd Add an extension to the dialplan"
[129]=>
string(66) " BridgeTechnologyUnsuspend Unsuspend a bridging technology."
[130]=>
string(64) " BridgeTechnologySuspend Suspend a bridging technology."
[131]=>
string(80) " BridgeTechnologyList List available bridging technologies and their"
[132]=>
string(61) " DataGet Retrieve the data api tree."
[133]=>
string(47) " DBPut Put DB entry."
[134]=>
string(49) " DBDelTree Delete DB Tree."
[135]=>
string(50) " DBDel Delete DB entry."
[136]=>
string(47) " DBGet Get DB Entry."
}
array(3) {
[0]=>
string(132) " Username IP Address Start Elapsed FileDes HttpCnt Read Write"
[1]=>
string(142) " cxpanel 127.0.0.1 1426370041 5167 19 0 2147483647 2147483647"
[2]=>
string(18) "1 users connected."
}
array(16) {
[0]=>
string(11) "Usecount: 1"
[1]=>
string(16) "Category: 262144"
[2]=>
string(6) "Event:"
[3]=>
string(21) "Event: SuccessfulAuth"
[4]=>
string(23) "Privilege: security,all"
[5]=>
string(37) "EventTV: 2015-03-15T05:22:02.054+0600"
[6]=>
string(23) "Severity: Informational"
[7]=>
string(12) "Service: AMI"
[8]=>
string(15) "EventVersion: 1"
[9]=>
string(16) "AccountID: admin"
[10]=>
string(25) "SessionID: 0x7ff0f0000ad8"
[11]=>
string(35) "LocalAddress: IPV4/TCP/0.0.0.0/5038"
[12]=>
string(39) "RemoteAddress: IPV4/TCP/127.0.0.1/39453"
[13]=>
string(16) "UsingPassword: 0"
[14]=>
string(39) "SessionTV: 2015-03-15T05:22:02.054+0600"
[15]=>
string(0) ""
}
array(49) {
[0]=>
string(7) "Events:"
[1]=>
string(66) " -------------------- -------------------- --------------------"
[2]=>
string(51) " AGIExecEnd AGIExecStart AOC-D"
[3]=>
string(57) " AOC-E AOC-S AgentCalled"
[4]=>
string(55) " AgentComplete AgentConnect AgentDump"
[5]=>
string(63) " AgentLogin AgentLogoff AgentRingNoAnswer"
[6]=>
string(51) " Agents AgentsComplete Alarm"
[7]=>
string(57) " AlarmClear AorDetail AsyncAGIEnd"
[8]=>
string(62) " AsyncAGIExec AsyncAGIStart AttendedTransfer"
[9]=>
string(59) " AuthDetail AuthMethodNotAllowed BlindTransfer"
[10]=>
string(57) " BridgeCreate BridgeDestroy BridgeEnter"
[11]=>
string(59) " BridgeLeave ChallengeResponseFai ChallengeSent"
[12]=>
string(65) " ChanSpyStart ChanSpyStop ChannelTalkingStart"
[13]=>
string(60) " ChannelTalkingStop ConfbridgeEnd ConfbridgeJoin"
[14]=>
string(62) " ConfbridgeLeave ConfbridgeMute ConfbridgeRecord"
[15]=>
string(63) " ConfbridgeStart ConfbridgeStopRecord ConfbridgeTalking"
[16]=>
string(61) " ConfbridgeUnmute ContactStatusDetail CoreShowChannel"
[17]=>
string(54) " CoreShowChannelsComp DAHDIChannel DNDState"
[18]=>
string(53) " DeviceStateChange DialBegin DialEnd"
[19]=>
string(61) " EndpointDetail EndpointList ExtensionStatus"
[20]=>
string(62) " FAXSession FAXSessionsComplete FAXSessionsEntry"
[21]=>
string(55) " FAXStats FAXStatus FailedACL"
[22]=>
string(62) " FullyBooted Hangup HangupHandlerPop"
[23]=>
string(59) " HangupHandlerPush HangupHandlerRun HangupRequest"
[24]=>
string(62) " Hold IdentifyDetail InvalidAccountID"
[25]=>
string(62) " InvalidPassword InvalidTransport LoadAverageLimit"
[26]=>
string(66) " LocalBridge LocalOptimizationBeg LocalOptimizationEnd"
[27]=>
string(60) " MCID MWIGet MWIGetComplete"
[28]=>
string(57) " MeetmeEnd MeetmeJoin MeetmeLeave"
[29]=>
string(59) " MeetmeMute MeetmeTalkRequest MeetmeTalking"
[30]=>
string(58) " MemoryLimit MiniVoiceMail MonitorStart"
[31]=>
string(61) " MonitorStop MusicOnHoldStart MusicOnHoldStop"
[32]=>
string(54) " NewAccountCode NewCallerid NewExten"
[33]=>
string(63) " Newchannel Newstate OriginateResponse"
[34]=>
string(63) " ParkedCall ParkedCallGiveUp ParkedCallTimeOut"
[35]=>
string(65) " PeerStatus Pickup PresenceStateChange"
[36]=>
string(61) " PresenceStatus QueueCallerAbandon QueueCallerJoin"
[37]=>
string(62) " QueueCallerLeave QueueMemberAdded QueueMemberPause"
[38]=>
string(66) " QueueMemberPenalty QueueMemberRemoved QueueMemberRinginuse"
[39]=>
string(54) " QueueMemberStatus RTCPReceived RTCPSent"
[40]=>
string(52) " ReceiveFAX Registry Reload"
[41]=>
string(65) " RequestBadFormat RequestNotAllowed RequestNotSupported"
[42]=>
string(58) " SIPQualifyPeerDone SendFAX SessionLimit"
[43]=>
string(63) " SessionTimeout Shutdown SoftHangupRequest"
[44]=>
string(52) " SpanAlarm SpanAlarmClear Status"
[45]=>
string(61) " StatusComplete SuccessfulAuth TransportDetail"
[46]=>
string(52) " UnParkedCall UnexpectedAddress Unhold"
[47]=>
string(52) " UserEvent VarSet VarSet"
[48]=>
string(8) " VarSet"
}
array(17) {
[0]=>
string(0) ""
[1]=>
string(16) "Global Settings:"
[2]=>
string(16) "----------------"
[3]=>
string(32) " Manager (AMI): Yes"
[4]=>
string(31) " Web Manager (AMI/HTTP): No"
[5]=>
string(41) " TCP Bindaddress: 0.0.0.0:5038"
[6]=>
string(31) " HTTP Timeout (minutes): 60"
[7]=>
string(31) " TLS Enable: No"
[8]=>
string(37) " TLS Bindaddress: Disabled"
[9]=>
string(41) " TLS Certfile: asterisk.pem"
[10]=>
string(17) " TLS Privatekey:"
[11]=>
string(13) " TLS Cipher:"
[12]=>
string(32) " Allow multiple login: Yes"
[13]=>
string(32) " Display connects: Yes"
[14]=>
string(31) " Timestamp events: No"
[15]=>
string(15) " Channel vars:"
[16]=>
string(31) " Debug: No"
}
array(8) {
[0]=>
string(0) ""
[1]=>
string(8) "username"
[2]=>
string(8) "--------"
[3]=>
string(5) "rinat"
[4]=>
string(5) "admin"
[5]=>
string(7) "cxpanel"
[6]=>
string(19) "-------------------"
[7]=>
string(27) "3 manager users configured."
}
array(9) {
[0]=>
string(0) ""
[1]=>
string(25) " username: admin"
[2]=>
string(25) " secret: <Set>"
[3]=>
string(23) " ACL: yes"
[4]=>
string(115) " read perm: system,call,log,verbose,command,agent,user,config,dtmf,reporting,cdr,dialplan,originate,message"
[5]=>
string(115) " write perm: system,call,log,verbose,command,agent,user,config,dtmf,reporting,cdr,dialplan,originate,message"
[6]=>
string(23) " displayconnects: yes"
[7]=>
string(23) "allowmultiplelogin: yes"
[8]=>
string(19) " Variables:"
}
Теперь начнем разбор полетов.
Простые функции у нас не будут обрабатываться, отдадим их как есть нашему клиенту.
Начнем коддинг.
<?php
function asterisk_cli_exec ($a)
{
exec("asterisk -rx '$a'", $b);
return $b;
}
function asterisk_cli_passthru ($a)
{
passthru("asterisk -rx '$a'", $b);
return $b;
}
function cli ($a)
{
/*
Матчасть:
trim - удаляет пробелы в начале и конце статьи
strtolower - преобразует строку в нижний регистр
str_replace - удаляет двойные пробелы
*/
$a=str_replace(" ", " ",trim(strtolower($a)));
/* переменую $b мы будем использовать в кострукции switch */
$b=-1;
/* Массив в котором мы будем хранить значения полученные из Астериска */
$result = array ();
/* массив где мы храним cli команды */
$ar = array ("manager reload",
"manager set debug",
"manager show command ",
"manager show event ", /* здесь простые команды заканчиваются, элемент массива № 3 */
"manager show connected",
"manager show eventq",
"manager show events",
"manager show settings",
"manager show users",
"manager show user ");
for ($i=0;$i<count($ar);$i++)
{
if (strpos($a,$ar[$i])!==false)
{
$b = $i;
}
}
switch (true)
{
case $b==-1: /* если мы не нашли команду то мы уходим из функции выдавая пустой массив */
$result[0]["Name"] = "Error";
$result[0]["Function"] = "cli";
$result[0]["Number"] = "00001";
$result[0]["Description"] = "Не найдена CLI команда";
return $result;
break;
case $b<=3:
return asterisk_cli_exec($a);
break;
}
}
var_dump(cli ('manager set debfsugfsdf')); /* заведомо ложная команда */
var_dump(cli ('manager set debug'));
var_dump(cli ('manager reload'));
?>
array(1) {
[0]=>
array(4) {
["Name"]=>
string(5) "Error"
["Function"]=>
string(3) "cli"
["Number"]=>
string(5) "00001"
["Description"]=>
string(38) "Не найдена CLI команда"
}
}
array(1) {
[0]=>
string(20) "manager debug is off"
}
array(0) {
}
Ну что же, продолжим дальше анализ данных поступающий от Астериска.
Беглый взгляд позволяет найти «похожие» ответы на команды.
Лично я составил несколько групп:
1 группа
manager show eventq
manager show settings
manager show user
(похожесть состоит в разделении ключей символом ":"-двоеточие)
2 группа
manager show commands
manager show events
(похожесть состоит в разделении ключей символом " "-пробелом(лами))
и без групп
manager show users
manager show connected
В связи с этим нам нужно реструктуризировать наш массив, где мы храним CLI команды, таким образом:
$ar = array ("manager reload",
"manager set debug",
"manager show command ",
"manager show event ", /* здесь простые команды заканчиваются, элемент массива № 3 */
"manager show eventq", /* 1 группа */
"manager show settings",/* 1 группа */
"manager show user ", /* 1 группа */
"manager show commands", /* 2 группа */
"manager show events", /* 2 группа */
"manager show users", /* Без группы */
"manager show connected" /* Без группы */
);
Приступим к структуризации получаемых данных из 1 группы.
<?php
function asterisk_cli_exec ($a)
{
exec("asterisk -rx '$a'", $b);
return $b;
}
function asterisk_cli_passthru ($a)
{
passthru("asterisk -rx '$a'", $b);
return $b;
}
function cli ($a)
{
/*
Матчасть:
trim - удаляет пробелы в начале и конце статьи
strtolower - преобразует строку в нижний регистр
str_replace - удаляет двойные пробелы
*/
$a=str_replace(" ", " ",trim(strtolower($a)));
/* переменую $b мы будем использовать в кострукции switch */
$b=-1;
/* Массив в котором мы будем хранить значения полученные из Астериска */
$result = array ();
/* массив где мы храним cli команды */
$ar = array ("manager reload",
"manager set debug",
"manager show command ",
"manager show event ", /* здесь простые команды заканчиваются, элемент массива № 3 */
"manager show eventq", /* 1 группа */
"manager show settings",/* 1 группа */
"manager show user ", /* 1 группа */
"manager show commands", /* 2 группа */
"manager show events", /* 2 группа */
"manager show users", /* Без группы */
"manager show connected" /* Без группы */
);
for ($i=0;$i<count($ar);$i++)
{
if (strpos($a,$ar[$i])!==false)
{
$b = $i;
}
}
switch (true)
{
case $b==-1: /* если мы не нашли команду то мы уходим из функции выдавая пустой массив */
$result[0]["Name"] = "Error";
$result[0]["Function"] = "cli";
$result[0]["Number"] = "00001";
$result[0]["Description"] = "Не найдена CLI команда";
return $result;
break;
case $b<=3:
return asterisk_cli_exec($a);
break;
case $b<=6:
/* Матчасть
isset - проверяет существует ли переменная
unset - разрушает переменную
count - вычисляет длину массива
explode - делит строку на подстроки используя разделитель, в итоге получается массив
*/
$t = asterisk_cli_exec($a); /* Получаем результаты выполнения команды в временный массив $t */
while (isset($t[0]))
{
if ($t[0]!="") /* Проверяем не является ли текущая строка пустой*/
{
unset($m); /* Уничтожаем переменную чтобы при повторном обращении не появились остаточные данные */
$m = explode(":", $t[0]); /* Делим строку на массив используя разделитель символ ":" */
if (isset($m[1])) /* Проверяем существует ли значение, если его нет то или это не наши данные а просто строка или это данные с пустым значением что нам также не нужно */
{
$n = $m[1]; /* Присваиваем в переименую начало значения */
for ($i=2;$i<count($m);$i++)
{
$n.=":".$m[$i]; /* Если имеются еще элементы массива то мы восстанавливаем значение */
}
$result[trim($m[0])]=trim($n); /* Присваиваем значение и ключ в наш массив */
}
}
array_splice($t, 0, 1);
}
return $result;
break;
}
}
var_dump(cli ('manager show user admin'));
var_dump(cli ('manager show eventq'));
var_dump(cli ('manager show settings'));
?>
array(6) {
["username"]=>
string(5) "admin"
["secret"]=>
string(5) "<Set>"
["acl"]=>
string(3) "yes"
["read perm"]=>
string(91) "system,call,log,verbose,command,agent,user,config,dtmf,reporting,cdr,dialplan,originate,all"
["write perm"]=>
string(91) "system,call,log,verbose,command,agent,user,config,dtmf,reporting,cdr,dialplan,originate,all"
["displayconnects"]=>
string(3) "yes"
}
array(8) {
["Usecount"]=>
string(1) "1"
["Category"]=>
string(1) "1"
["Event"]=>
string(8) "Registry"
["Privilege"]=>
string(10) "system,all"
["Timestamp"]=>
string(17) "1426383271.261620"
["ChannelType"]=>
string(3) "SIP"
["Domain"]=>
string(13) "217.15.180.50"
["Status"]=>
string(10) "Registered"
}
array(16) {
["Global Settings"]=>
string(0) ""
["Manager (AMI)"]=>
string(3) "Yes"
["Web Manager (AMI/HTTP)"]=>
string(2) "No"
["TCP Bindaddress"]=>
string(12) "0.0.0.0:5038"
["HTTP Timeout (minutes)"]=>
string(2) "60"
["TLS Enable"]=>
string(2) "No"
["TLS Bindaddress"]=>
string(8) "Disabled"
["TLS Certfile"]=>
string(12) "asterisk.pem"
["TLS Privatekey"]=>
string(0) ""
["TLS Cipher"]=>
string(0) ""
["Allow multiple login"]=>
string(3) "Yes"
["Display connects"]=>
string(3) "Yes"
["Timestamp events"]=>
string(3) "Yes"
["Channel vars"]=>
string(0) ""
["Debug"]=>
string(2) "No"
["Block sockets"]=>
string(2) "No"
}
Алгоритм развивается дальше в том же направлении и при использовании данных команд. К сожалению, коддеры Астера очень часто меняют вывод результатов команд, иногда даже по нескольку раз в течении одной версии. Для неповторяющихся результатов команд ветка алгоритма пишется отдельно.
В данной статье не рассмотрены 2 группа и неповторяющиеся команды. Но основная идея с реализацией видна.
Резюме
Написать модуль для связи Астериска с веб-приложением можно и для этого не требуются глубокие познания в Звездочке или PHP. Надеюсь, данная статья поможет вам понять, в какую сторону идти и как связать серверную часть приложения с Астериском.
p.s. Конечно, легче всего все реализовать на сокетах и AMI, но с функционалом CLI на начальном уровне знакомства с Астериском пока ничего не может сравниться. Разве что прямое редактирование конфигурационных файлов или SQL.
Защитникам ARI и его производных скажу сразу свое мнение, которое не претендует на истину в последней инстанции, реализация приложений на них требует более глубокого понимания Звездочки.
Просьба не кидаться камнями, я просто поделился своим опытом. Кстати, начинающим кодерам и VOIP-програмерам — «код полностью рабочий», проверено на Астере 13 версии.
Автор: RinatUsmanov
спасибо