Postgres用PHP触发通知

时间:2014-05-09 18:49:07

标签: postgresql plpgsql database-trigger

我对Postgre触发器有疑问或误解 - >执行通知 - >捕获到PHP流程。

我的平台是PHP(5.6)与Postgres的中心。

我必须添加带有通知表的触发器,并且只要新通知添加到该通知短信必须发送给该用户。

所以这里添加了像这样的触发器

CREATE FUNCTION xxx_sms_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
DECLARE
BEGIN
  PERFORM pg_notify('sms', NEW.id||'' );
  RETURN new;
END;

并在php中插入新通知正常。

现在我有一个单独的文件,其中添加了捕获pg_notify由“pg_get_notify”触发,这里我无法得到这个流程就像Postgres如何触发一些未知的PHP脚本而不运行它服务或我如何使其发挥作用?

5 个答案:

答案 0 :(得分:3)

您确实需要一个作为服务运行的PHP脚本。如果这将是接收您提供的通知的语言。正如@FelipeRosa所说,该脚本需要连接到数据库,然后发出至少一个命令:

listen sms;

主网站上有一个很好的监听示例(http://www.php.net/manual/en/function.pg-get-notify.php

几年后我没有在php中编码。最近我在python中实现了这个逻辑,但它应该是一样的。我做了一点研究,我可以在php中找到select(),但似乎在php中没有postgres套接字描述符,所以你不能在php中使用select(),除非你能找到postgres套接字描述符

无论如何,该线程就在这里(http://postgresql.1045698.n5.nabble.com/Is-there-any-way-to-listen-to-NOTIFY-in-php-without-polling-td5749888.html)。有一个轮询示例,你的PHP脚本端靠近底部。你可以像之前选择的那样进行监听(一次),然后将你的pg_get_notify()置于一个有睡眠的循环中,持续你愿意排队通知的时间。

只是fwiw,在python我没有轮询,我select.select(pg_conn,...),当数据到达postgres连接时,我检查它是否有通知,所以没有'轮询'。如果你能找到一种在php中使用select()而不是循环的方法,那就太好了。

-g

答案 1 :(得分:1)

这是一个有凝聚力的例子,它记录对表插入的兴趣,等待通知(或超时)并响应调用者。我们使用前面带有字母“C”的时间戳来标识通知通道,因为Postgres要求通道名称是正确的标识符。

Postgres SQL

/*  We want to know when items of interest get added to this table.   
    Asynchronous insertions possible from different process or server  */
DROP TABLE IF EXISTS History;
CREATE TABLE History (
   HistoryId INT PRIMARY KEY,
   MYKEY CHAR(17),
   Description TEXT,
   TimeStamp BIGINT
);

/*  Table of registered interest in a notification  */
DROP TABLE IF EXISTS Notifications;
CREATE TABLE Notifications (
   NotificationId INT PRIMARY KEY,
   Channel VARCHAR(20),
   MYKEY CHAR(17)
);

/*  Function to process a single insertion to History table  */
CREATE OR REPLACE FUNCTION notify_me()
  RETURNS trigger AS
$BODY$
  DECLARE ch varchar(20);
  BEGIN
     FOR ch IN
     SELECT DISTINCT Channel FROM Notifications 
        WHERE MYKEY=NEW.MYKEY
     LOOP
        /* NOTIFY ch, 'from notify_me trigger'; */
        EXECUTE 'NOTIFY C' || ch || ', ' || quote_literal('from notify_me') || ';';
        DELETE FROM Notifications WHERE Channel=ch;
     END LOOP;
     RETURN NULL;
  END;
$BODY$
LANGUAGE 'plpgsql';

/*  Trigger to process all insertions to History table  */
DROP TRIGGER IF EXISTS HistNotify ON History CASCADE;
CREATE TRIGGER HistNotify AFTER INSERT ON History
    FOR EACH ROW EXECUTE PROCEDURE notify_me();

PHP代码

// $conn is a PDO connection handle to the Postgres DB
// $MYKEY is a key field of interest
$TimeStamp = time();  //  UNIX time (seconds since 1970) of the request
$timeout = 120;  //  Maximum seconds before responding

//  Register our interest in new history log activity
$rg = $conn->prepare("INSERT INTO Notifications (MYKEY, Channel)  VALUES (?,?)");
$rg->execute(array($MYKEY, $TimeStamp));

//  Wait until something to report
$conn->exec('LISTEN C'.$TimeStamp.';');  //  Prepend ‘C’ to get notification channel
$conn->exec('COMMIT;');  //  Postgres may need this to start listening
$conn->pgsqlGetNotify (PDO::FETCH_ASSOC, $timeout*1000);  //  Convert  from sec to ms

//  Unregister our interest
$st = $conn->prepare("DELETE FROM Notifications WHERE Channel=?");  
$st->execute(array($TimeStamp));

答案 2 :(得分:1)

以下是如何将@Greg提到的“Python方式”迁移到PHP的示例。启动下面的脚本后 - 打开与postgres db的新连接并查询NOTIFY "test", 'I am the payload'

来源:

<?php

$dsn = 'user=postgres dbname=postgres password=postgres port=5432 host=localhost';

$connection = \pg_connect($dsn);

if (\pg_connection_status($connection) === \PGSQL_CONNECTION_BAD) {
    throw new \Exception(
        sprintf('The database connect failed: %s', \pg_last_error($connection))
    );
}

\pg_query('LISTEN "test"');

while (true) {
    $read = [\pg_socket($connection)];
    $write = null;
    $except = null;
    $num = \stream_select(
        $read,
        $write,
        $except,
        60
    );
    if ($num === false) {
        throw new \Exception('Error in optaining the stream resource');
    }
    if (\pg_connection_status($connection) !== \PGSQL_CONNECTION_OK) {
        throw new \Exception('pg_connection_status() is not PGSQL_CONNECTION_OK');
    } elseif ($num) {
        $notify = \pg_get_notify($connection);
        if ($notify !== false) {
            var_dump($notify);
        }
    }
}

答案 3 :(得分:0)

根据this,您应首先通过pg_query使应用程序侦听发出命令&#34; LISTEN&#34;的所需通道,然后才能将消息通知给应用程序。

答案 4 :(得分:0)

这是一个小例子:

PHP脚本(我将其命名为teste.php - http://php.net/manual/pt_BR/function.pg-get-notify.php处的内容相同):

$conn = pg_pconnect("dbname=mydb");
if (!$conn) {
  echo "An error occurred.\n";
  exit;
}

while(true){
   pg_query($conn, 'LISTEN SMS;');
   $notify = pg_get_notify($conn);
     if (!$notify) {
       echo "No messages\n";
       // change it as u want
     } else {
       print_r($notify);
       //your code here
     }
     sleep(2);
}

保持脚本runnig(我以为你使用的是linux):

php teste.php > log.txt 2>&1 &

请注意:

2&gt;&amp; 1 将标准输出和标准错误重定向到log.txt文件中。

&amp; 在后台运行整个事情

您可以使用以下命令执行log.txt:

tail -f log.txt