SAP Remote Function Modules Calls made easy using sapnwrfc.

v0.1.1 2016-04-07 15:59 UTC

This package is not auto-updated.

Last update: 2024-11-09 19:51:15 UTC


README

SAP Remote Function Modules Calls made easy using sapnwrfc and PHP.

Latest Stable Version Total Downloads Latest Unstable Version License

Legal notice

SAP and other SAP products and services mentioned herein are trademarks or registered trademarks of SAP SE (or an SAP affiliate company) in Germany and other countries.

Summary

Welcome to SapPhp package. This packages is not a connector, it uses php-sapnwrfc extension to handle client - server communication. This package is intended to provide a clean object oriented interface to handle extensive data extraction using RFC calls. My development plan is to extend this class with PHP Interfaces to SAP FMs (check RfcReadTable interface)

This is an early version and I expect you to raise issues and bugs and maybe give me some suggestions.

Install

Make sure you have the php-sapnwrfc extension installed.

composer require avdaneidanut/sapphp

SAP Systems details

The package uses two methods for retrievieng SAP Systems details (ashost, sysnr, description and name) by parsing files using the \SapPhp\Repository class.

  1. Parsing saplogon.ini file from: C:/Users/{currentUser}/AppData/Roaming/SAP/Common/.

  2. Parsing sapphp.xml from package root folder.

If the first method fails or returns no result the second method will be performed.

Connecting to SAP

<?php

use SapPhp\Connection;
use SapPhp\Exceptions\BoxNotFoundException;

try {
	$connection = new Connection(
		'box', // SAP Box Name
		'user', // SAP Username
		'passwd', // SAP Password
		'500' // SAP Client Code
	);
} catch(sapnwrfcConnectionException $ex) {
	// Do something if login failed.
} catch(BoxNotFoundException $ex) {
	// Do something if box doesn't exist.
}

Perform Function Module call

Let's get details about an user:

<?php

// ... connection

// Instantiate new Function Module interface.
$function = $connection->fm(
	'BAPI_USER_GET_DETAIL', // RFC Enable FM
	true // Parse result (trim all strings and decode GUIDs)
);

// Get function description.
print_r($function->description());

// Add import parameter.
// Will trigger an \SapPhp\Exceptions\ParamNotFoundException if param is not found in function description.
$function->param('USERNAME', 'USER');

// Perform function call and retrieve result.
$result = $function->invoke();

How about getting details about an user using RFC_READ_TABLE FM? Let's go:

<?php

// ... connection

$function = $connection->fm('RFC_READ_TABLE');

$function->param('QUERY_TABLE', 'USR01')
	->param('OPTIONS', [
		['TEXT' => 'BNAME = \'USER\' OR BNAME = \'USER2\' OR BNAME LIKE \'USER5*\'']
	])
	->param('ROWCOUNT', 5)
	->param('DELIMITER', '~')
;

$result = $function->invoke();

Very nice, we can query a table using a SQL statement. The result from this FM is dirty, fix it with explodes and array_merge, right?

[
  "DATA" => [
    [
      "WA" => "500~USER2      ~                    ~    ~ ~H~K~1~ ~        ~   ~            ~ ~                    ~                    ~ ~ ~0",
    ],
    [
      "WA" => "500~USER5      ~                    ~    ~ ~H~K~1~ ~        ~   ~            ~ ~                    ~                    ~ ~ ~0",
    ],
    [
      "WA" => "500~USER55      ~                    ~    ~ ~H~K~1~ ~        ~   ~            ~ ~                    ~                    ~ ~ ~0",
    ],
  ],
  "FIELDS" => [
    [
      "FIELDNAME" => "MANDT",
      "OFFSET" => "000000",
      "LENGTH" => "000003",
      "TYPE" => "C",
      "FIELDTEXT" => "Client",
    ],
    [
      "FIELDNAME" => "BNAME",
      "OFFSET" => "000004",
      "LENGTH" => "000012",
      "TYPE" => "C",
      "FIELDTEXT" => "User Name in User Master Record",
    ],
    [
      "FIELDNAME" => "STCOD",
      "OFFSET" => "000017",
      "LENGTH" => "000020",
      "TYPE" => "C",
      "FIELDTEXT" => "Start menu (old, replaced by XUSTART)",
    ],
    [
      "FIELDNAME" => "SPLD",
      "OFFSET" => "000038",
      "LENGTH" => "000004",
      "TYPE" => "C",
      "FIELDTEXT" => "Spool: Output device",
    ],
    [
      "FIELDNAME" => "SPLG",
      "OFFSET" => "000043",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Print parameter 1",
    ],
    [
      "FIELDNAME" => "SPDB",
      "OFFSET" => "000045",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Print parameter 2",
    ],
    [
      "FIELDNAME" => "SPDA",
      "OFFSET" => "000047",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Print parameter 3",
    ],
    [
      "FIELDNAME" => "DATFM",
      "OFFSET" => "000049",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Date format",
    ],
    [
      "FIELDNAME" => "DCPFM",
      "OFFSET" => "000051",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Decimal notation",
    ],
    [
      "FIELDNAME" => "HDEST",
      "OFFSET" => "000053",
      "LENGTH" => "000008",
      "TYPE" => "C",
      "FIELDTEXT" => "Host destination",
    ],
    [
      "FIELDNAME" => "HMAND",
      "OFFSET" => "000062",
      "LENGTH" => "000003",
      "TYPE" => "C",
      "FIELDTEXT" => "Default host client",
    ],
    [
      "FIELDNAME" => "HNAME",
      "OFFSET" => "000066",
      "LENGTH" => "000012",
      "TYPE" => "C",
      "FIELDTEXT" => "Default host user name",
    ],
    [
      "FIELDNAME" => "MENON",
      "OFFSET" => "000079",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Automatic Start",
    ],
    [
      "FIELDNAME" => "MENUE",
      "OFFSET" => "000081",
      "LENGTH" => "000020",
      "TYPE" => "C",
      "FIELDTEXT" => "Menu name",
    ],
    [
      "FIELDNAME" => "STRTT",
      "OFFSET" => "000102",
      "LENGTH" => "000020",
      "TYPE" => "C",
      "FIELDTEXT" => "Start menu (old, replaced by XUSTART)",
    ],
    [
      "FIELDNAME" => "LANGU",
      "OFFSET" => "000123",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Language",
    ],
    [
      "FIELDNAME" => "CATTKENNZ",
      "OFFSET" => "000125",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "CATT: Test status",
    ],
    [
      "FIELDNAME" => "TIMEFM",
      "OFFSET" => "000127",
      "LENGTH" => "000001",
      "TYPE" => "C",
      "FIELDTEXT" => "Time Format (12-/24-Hour Specification)",
    ],
  ],
  "OPTIONS" => [
    [
      "TEXT" => "TEXT' => 'BNAME = 'USER' OR BNAME = 'USER2' OR BNAME LIKE 'USER5*'",
    ],
  ],
]

But wait, how about using a FunctionModule interface that has a query builder and parses the result?

<?php

// ... connection

// fm method will check if RfcReadTable is an FunctionModule interface Class, if so will return a new instance.
$function = $connection->fm('RfcReadTable'); 

// Let's do the same thing as before.
$result = $function->table('usr01') // set the query table
	->where('bname', ['USER', 'USER5']) // add multiple where clause (simulating where in )
	->orWhere('bname', 'LIKE', 'USER5*') // add custom comparation operator
	->limit(5) // limit the result to 5 rows
	->get() // perform function call, parse the result and return a \Illuminate\Support\Collection object.
;

print_r($result->toArray());

And the result:

[
	[
		"MANDT" => "500",
		"BNAME" => "USER2",
		"STCOD" => "",
		"SPLD" => "",
		"SPLG" => "",
		"SPDB" => "H",
		"SPDA" => "K",
		"DATFM" => "1",
		"DCPFM" => "",
		"HDEST" => "",
		"HMAND" => "",
		"HNAME" => "",
		"MENON" => "",
		"MENUE" => "",
		"STRTT" => "",
		"LANGU" => "",
		"CATTKENNZ" => "",
		"TIMEFM" => "0",
	],
	[
		"MANDT" => "500",
		"BNAME" => "USER5",
		"STCOD" => "",
		"SPLD" => "",
		"SPLG" => "",
		"SPDB" => "H",
		"SPDA" => "K",
		"DATFM" => "1",
		"DCPFM" => "",
		"HDEST" => "",
		"HMAND" => "",
		"HNAME" => "",
		"MENON" => "",
		"MENUE" => "",
		"STRTT" => "",
		"LANGU" => "",
		"CATTKENNZ" => "",
		"TIMEFM" => "0",
	],
	[
		"MANDT" => "500",
		"BNAME" => "USER55",
		"STCOD" => "",
		"SPLD" => "",
		"SPLG" => "",
		"SPDB" => "H",
		"SPDA" => "K",
		"DATFM" => "1",
		"DCPFM" => "",
		"HDEST" => "",
		"HMAND" => "",
		"HNAME" => "",
		"MENON" => "",
		"MENUE" => "",
		"STRTT" => "",
		"LANGU" => "",
		"CATTKENNZ" => "",
		"TIMEFM" => "0",
	],
]

Take a look at RfcReadTable and QueryBuilder methods.

QueryBuilder usage.

<?php

$query->where('column', 'value') // Add WHERE clause
	->andWhere('column2,' 'value2') // AND logical operarator
	->orWhere('column3', '<>', 'value3') // OR logical operator
	->orWhere(function ($query) { // WHERE group
		$query->where('column11', 'value11')
			->andWhere('column22', 'value22');
	})
	->orWhere('column5', '<>', [1, 2, 3, 4]); // Simulate WHERE IN clause

The previous code will generate the folowing SQL query:

	WHERE
		COLUMN = 'value' 
		AND
		COLUMN2 = 'value2'
		OR
		COLUMN3 <> 'value3'
		OR
		(
			COLUMN11 = 'value11'
			AND
			COLUMN22 = 'value22'
		)
		OR 
		(
			COLUMN5 <> '1'
			OR
			COLUMN5 <> '2'
			OR
			COLUMN5 <> '3'
			OR
			COLUMN5 <> '4'
		)

To-do

  • Aggregate multiple table results in one Collection and share the same query over multiple tables.
<?php

// ... connection & function

$rfcReadTable->table(['table1', 'table2', 'table3'], function($agregatter) {
	$aggregate->table('table1')
		->with('table2')
		->on('column')
		->as('aggregated_table');
	$aggregate->table('aggregated_table', 'table3')
		->on('column2');
})->where('column', 'value');
  • Add mode FunctionModule interfaces as RfcReadTable - Please send suggestions!

Support

I will help you ASAP if you find any issues in using this package.