rtcphp.pas

来自「Delphi快速开发Web Server」· PAS 代码 · 共 677 行 · 第 1/2 页

PAS
677
字号
{
  @html(<b>)
  PHP Scripting
  @html(</b>)
  - Copyright (c) Danijel Tkalcec
  @html(<br><br>)

  Based on 'PHP 4 Delphi'
    by Serhiy Perevoznyk
    <serge_perevoznyk(at)hotmail.com>
    <http://users.chello.be/ws36637>
  @html(<br>)

  You have to download and install PHP separately.
  It is not included in the package.
  You can download the latest version of PHP from
    <http://www.php.net/downloads.php>
  @html(<br>)

  For more information on the PHP Group and the PHP project,
  please see <http://www.php.net>.
}

unit rtcPHP;

{$INCLUDE rtcDefs.inc}

interface

uses
  rtcTrashcan,
  
  Windows, SysUtils, Classes,

  rtcLog,
  
  rtcInfo,
  rtcPHPTypes,

  rtcThrPool;

type
  { @abstract(All exceptions raised by PHP class will be of this type)
    @exclude }
  EPHPError = class(Exception);

  { @abstract(Provides access to PHP5 file scripting)
    Can be easily used with @Link(TRtcHttpServer) to add
    PHP5 functionality to your HTTP Server. You can create
    as many @name components as you need, as long as each
    of them is created from a different thread. @longcode(#
    IMPORATANT:
      When using TRtcPHP, especially in a multithreaded server,
      YOU HAVE TO create a (temporary) component before processing a
      request and destroy it after the request has been processed.

    NOTE:
      There will be NO EXECUTION SPEED DRAWBACKS from this approach,
      because there is no special initialization in TRtcPHP constructor.
      The only thing that is being initialized in the TRtcPHP
      component are the properties you have to pass to the component.
      Everyting else is initialized from the StatupPHP procedure.

    EXAMPLE:

      function Parse_PHP(Sender:TRtcHttpServer;
                         const PhpFileName, DocRoot:string;
                         var Header:string):string;
        var
          PHP:TRtcPHP;
        begin
        // Create the PHP sripting component
        PHP:=TRtcPHP.Create;
        try
          // Prepare the script for execution
          PHP.FileName:=PhpFileName;
          PHP.DocumentRoot:=DocRoot;

          PHP.LocalAddr:=Sender.LocalAddr;
          PHP.LocalPort:=Sender.LocalPort;
          PHP.PeerAddr:=Sender.PeerAddr;
          PHP.PeerPort:=Sender.PeerPort;

          PHP.Request:=Sender.Request;
          PHP.RequestBody:=Sender.Read;

          // Execute the script
          Result:=PHP.Execute;

          // Get HTTP Header prepared by PHP
          Header:=PHP.ResultHeader;
        finally
          // Release the PHP scripting component
          PHP.Free;
          end;
        end; #) }
  TRtcPHP = class
  private
    FRequest:TRtcRequest;

    FRequestBody : string;

    FPeerAddr:string;
    FPeerPort:string;
    FLocalAddr : string;
    FLocalPort : string;

    FDocumentRoot : string;
    FResultHeader : string;

    FSessionActive : boolean;
    TSRMLS_D  : pppointer;

    FBuffer : TStringList;
    FBufferSize : integer;

    FFileName : string;

    procedure StartupRequest; virtual;
    procedure ShutdownRequest; virtual;

  protected
    { PHP reads posted Body content chunk-wise.
      This variable is used by TRtcPHP to track
      how much of our RequestBody is already out.
      @exclude }
    RequestBodyOut:integer;

  public
    { Create the PHP scripting object }
    constructor Create;
    { Destroy object }
    destructor Destroy; override;

    { EXECUTE the script from file @Link(FileName).
      Before calling Execute, the PHP Library has to
      be initialized by calling @Link(StartupPHP) and
      all TRtcPHP properties have to be filled with valid data.
      Additionally to the resulting HTML page, PHP generates a
      HTML header, accessible from the @Link(ResultHeader) property,
      immediatelly after calling @name.
      @return(HTML output as string) }
    function  Execute : string;

    { SET: Filename to execute, including full path (no relative paths!). }
    property  FileName  : string read FFileName write FFileName;

    { SET: Request information, needed for script execution.
      When using TRtcPHP to execute server-side PHP scripts, simply assign
      the @Link(TRtcDataServer.Request) object from your @Link(TRtcHttpServer)
      connection component.
      This information is used to fill PHP variables. }
    property  Request: TRtcRequest read FRequest write FRequest;

    { SET: Local IP address. Same as @Link(TRtcConnection.LocalAddr).
      When using PHP with a connection component, simply assign the
      connection component's @Link(TRtcConnection.LocalAddr) property.
      This information is used to fill PHP variables. }
    property  LocalAddr : string read FLocalAddr write FLocalAddr;
    { SET: Local Port. Same as @Link(TRtcConnection.LocalPort).
      When using PHP with a connection component, simply assign the
      connection component's @Link(TRtcConnection.LocalAddr) property.
      This information is used to fill PHP variables. }
    property  LocalPort : string read FLocalPort write FLocalPort;
    { SET: Peer (remote PC) Address. Same as @Link(TRtcConnection.PeerAddr).
      When using PHP with a connection component, simply assign the
      connection component's @Link(TRtcConnection.PeerAddr) property.
      This information is used to fill PHP variables. }
    property  PeerAddr:string read FPeerAddr write FPeerAddr;
    { SET: Peer (remote PC) Port. Same as @Link(TRtcConnection.PeerPort).
      When using PHP with a connection component, simply assign the
      connection component's @Link(TRtcConnection.PeerPort) property.
      This information is used to fill PHP variables. }
    property  PeerPort:string read FPeerPort write FPeerPort;
    { SET: WebServer's Document Root folder.
      You should set this to your HttpServer's Document Root folder.
      This information is used to fill PHP variables. }
    property  DocumentRoot:string read FDocumentRoot write FDocumentRoot;

    { SET: HTTP Request Body, which was posted after the HTTP Header
      (also called Document Content). Before executing a PHP Request,
      wait for the whole Request to arrive at the server,
      so you can pass the whole Request Body to PHP.
      This information is used to fill PHP variables. }
    property  RequestBody : string read FRequestBody write FRequestBody;

    { GET: HTTP Request Header generated by PHP.
      For a HTTP Server to process PHP scripts correctly,
      in addition to passing all HTTP properties to the PHP scripting component,
      you have to return this header back to the client when sending the result. }
    property  ResultHeader : string read FResultHeader write FResultHeader;
  end;

{ Before you can use TRtcPHP components,
  you have to initialize the PHP Library by calling this procedure.
  This is usually done from the OnListenStart event handler
  of your Server connection component.

  @param(DLLFolder - Folder in which the DLL file for PHP5 resides)
  @param(IniFolder - Folder in which the INI file for PHP5 resides) }
procedure StartupPHP(DLLFolder,IniFolder:string);

{ To uninitialize the PHP Library, you should call this procedure.
  This is usually done from the OnListenStop event handler
  of your Server connection component. }
procedure ShutDownPHP;

implementation

uses
  memBinList,
  rtcSyncObjs;

var
  module_active : boolean = False;

  delphi_sapi_module : sapi_module_struct;
  ini_folder : string;
  CS:TRtcCritSec;
  ThrList:TBinList;

procedure InitResource;
  var
    p:pointer;
  begin
  CS.Enter;
  try
    if ThrList.search(GetCurrentThreadID)>0 then
      raise EPHPError.Create('InitResource: One thread, multiple resources.');
    p:=ts_resource_ex(0,nil);
    ThrList.insert(GetCurrentThreadID, longword(p));
  finally
    CS.Leave;
    end;
  end;

procedure FreeResource;
  var
    a:longword;
  begin
  CS.Enter;
  try
    a:=ThrList.search(GetCurrentThreadID);
    if a>0 then
      ThrList.remove(GetCurrentThreadID)
    else
      raise EPHPError.Create('FreeResource: Resource not existing.');
  finally
    CS.Leave;
    end;
  end;

function GetResource:pointer;
  var
    a:longword;
  begin
  Result:=nil;
  CS.Enter;
  try
    a:=ThrList.search(GetCurrentThreadID);
    if a>0 then
      Result:=pointer(a)
    else
      raise EPHPError.Create('GetResource: Undefined resource Thread!');
  finally
    CS.Leave;
    end;
  end;

function php_delphi_startup(sapi_module : Psapi_module_struct) : integer; cdecl;
  begin
  result := php_module_startup(sapi_module, nil, 0);
  end;

function php_delphi_deactivate(p : pointer) : integer; cdecl;
  begin
  result := 0;
  end;

function php_delphi_ub_write(str : ppointer; len : uint; p : pointer) : integer; cdecl;
  var
    ts : pointer;
    php : TRtcPHP;
    gl : psapi_globals_struct;
    s : string;
  begin
  try
    Result := 0;

    ts := GetResource;
    if not assigned(ts) then Exit;
    gl := GetSAPIGlobals(ts);
    if not assigned(gl) then Exit;
    php := TRtcPHP(gl^.server_context);
    if not assigned(php) then Exit;

    SetLength(s, len);
    if len>0 then
      Move(str^, s[1], len);

    try
      php.FBuffer.Add(s);
      Inc(php.FBufferSize, length(s));
    except
      end;

    Result := len;
  except
    on E:Exception do
      begin
      Log('Write callback',E,'PHP');
      Result:=0;
      end;
    end;
  end;

procedure php_delphi_register_variables(val : pzval; p : pointer); cdecl;
  var
    php : TRtcPHP;
    gl : psapi_globals_struct;
    ts : pointer;
    i : integer;
    vname, vval:string;
  begin
  try
    ts := GetResource;
    if not assigned(ts) then Exit;
    gl := GetSAPIGlobals(ts);
    if not assigned(gl) then Exit;
    php := TRtcPHP(gl^.server_context);
    if not assigned(php) then Exit;

    php_register_variable('SERVER_SOFTWARE', 'www.realthinclient.com', val, p);
    php_register_variable('SERVER_SIGNATURE', 'RealThinClient Library (c) Danijel Tkalcec', val, p);
    php_register_variable('SERVER_PROTOCOL', 'HTTP/1.1', val, p);

    if assigned(php.Request) then
      begin
      php_register_variable('REQUEST_METHOD',PChar(php.Request.Method), val, p);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?