Forum: PC-Programmierung PHP, ffi Beispiele


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Dergute W. (derguteweka)


Lesenswert?

Moin,

Ich haett gerne folgendes Problem (geloest):
Ich hab' folgende Files im rootdir meines Webservers:
1
root [ /srv/www ]# cat info.php errno.php printf.php 
2
<?php
3
phpinfo();
4
?>
5
6
7
<?php
8
// create FFI object, loading libc and exporting errno variable
9
$ffi = FFI::cdef(
10
    "int errno;", // this is a regular C declaration
11
    "libc.so.6");
12
// print C's errno
13
var_dump($ffi->errno);
14
?>
15
16
17
<?php
18
// create FFI object, loading libc and exporting function printf()
19
$ffi = FFI::cdef(
20
    "int printf(const char *format, ...);", // this is a regular C declaration
21
    "libc.so.6");
22
// call C's printf()
23
$ffi->printf("Hello %s!\n", "world");
24
?>
Die sind grossteils ge-c&p-t von hier:
https://www.php.net/manual/en/ffi.examples-basic.php
Aus einer shell aufgerufen mit
1
php xxx.php
 funktionieren alle 3 wie erwartet.
Aus einem Browser aufgerufen, funktioneren nur info.php und errno.php 
wie erwartet.
Bei printf.php seh' ich im Browser nix. Also nur eine weisse Seite.
In den logs seh' ich kein Genoergele.
Aber: Wenn ich errno.php nach printf.php aufrufe, ist errno 2.
Was laut /usr/include/asm-generic/errno-base.h das bedeutet:
1
#define ENOENT           2      /* No such file or directory */

Was geht'n da schief?

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Dergute W. schrieb:
> Bei printf.php seh' ich im Browser nix. Also nur eine weisse Seite.

Da da kein HTML-Header etc. kommt, ist das nicht weiter überraschend.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Harald K. schrieb:
> Dergute W. schrieb:
>> Bei printf.php seh' ich im Browser nix. Also nur eine weisse Seite.
>
> Da da kein HTML-Header etc. kommt, ist das nicht weiter überraschend.

Doch, ist es. Ich wuerde ein "Hello world" erwarten.
Bei errno.php kommt auch ein freundliches int(0) bzw. int(2).

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Vielleicht findet ja Dein php die libc.so.6 nicht, wenn es im Kontext 
des Webservers ausgeführt wird?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Ich hab' jetzt mal im printf.php die Zeile mit printf dahingehend 
abgeaendert:
1
var_dump($ffi->printf("Hello %s!\n", "world"));
Und kriege int(13) im Browser angezeigt.
Was ja der Rueckgabewert von printf sein wird. Also geht anscheinend der 
stdout der libc irgendwie nicht an php-fpm, so wie's aussieht? Aber 
warum, und wohin geht der?

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Nun, prüf' mal, welchen Wert stdout hat, also mach ein

var_dump($ffi->stdout);

von Dergute W. (derguteweka)


Lesenswert?

Moin,
Harald K. schrieb:
> Nun, prüf' mal, welchen Wert stdout hat, also mach ein
>
> var_dump($ffi->stdout);

Das sieht nicht gut aus. Im browser taucht weiterhin nix auf, aber 
stdout wird keine "normale" variable sein, vermute ich mal stark...
1
root [ /srv/www ]# php printf.php 
2
Hello world!
3
PHP Fatal error:  Uncaught FFI\Exception: Attempt to read undefined C variable 'stdout' in /srv/www/printf.php:8
4
Stack trace:
5
#0 {main}
6
  thrown in /srv/www/printf.php on line 8

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Dergute W. schrieb:
> aber
> stdout wird keine "normale" variable sein, vermute ich mal stark

stdout ist aber das, wohin printf seinen Kram ausgibt. Ist vom Typ 
FILE*, d.h. Pointer auf eine Struktur.

Statt printf("bla") kann man auch fprintf(stdout, "bla") verwenden.

Das wird nicht die Lösung Deines Problems sein, aber ich nehme stark an, 
daß das Problem irgendwo in diesem Dunstkreis liegt.

Dein Fehlercode 2 aus errno lässt jedenfalls darauf schließen, daß 
stdout nicht verfügbar ist.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Harald K. schrieb:
> Dein Fehlercode 2 aus errno lässt jedenfalls darauf schließen, daß
> stdout nicht verfügbar ist.

Ja, gut moeglich.
Koennte evtl. damit zusammenhaengen, wie nginx und php verwurstet sind?
hier meine /etc/nginx/nginx.conf
1
user www;
2
worker_processes  1;
3
#error_log  /var/log/nginx/error.log  debug;
4
#error_log  /var/log/nginx/error.log  info;
5
6
events {
7
    worker_connections  1024;
8
}
9
http {
10
11
    server {
12
        client_max_body_size 70m;
13
        listen       80;
14
        root /srv/www;
15
16
#fuer php zeugs:
17
        location ~* \.php$ {
18
            fastcgi_index   index.php;
19
            fastcgi_pass    127.0.0.1:9000;
20
            include         fastcgi.conf;
21
            fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
22
            fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
23
        }
24
        location / {
25
            index index.php index.html index.htm;
26
        }
27
    }
28
}

Da weiss ich nicht wirklich ob das alles so passt. Kann/muss man da 
evtl. noch "ein Kabel fuer stdout ziehen"?

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Dergute W. schrieb:
> Kann/muss man da
> evtl. noch "ein Kabel fuer stdout ziehen"?

Tut mir leid, das kann ich mangels Wissen & Erfahrung wirklich nicht 
beantworten - nginx hab' ich noch nie verwendet, und mein Wissen über 
php/apache ist auch eher an meinen Bedarf angepasst.

Brauchst Du denn printf, oder könntest Du das, was Du ausgeben willst, 
nicht auch anders transportieren?

sprintf beispielsweise kann in ein char-Array ausgeben, und das wiederum 
sollte man mit var_dump sehen können.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Harald K. schrieb:
> Brauchst Du denn printf,

Nee, nicht wirklich.
Ist nur irgendwie kein schoenes Gefuehl, wenn man schon bei den simplen 
copy+paste Beispielen aus der doku solche eigenartigen Effekte hat...

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Nun, das Beispiel funktioiert ja. Mit php.
Nur nicht mit dem in Deinen Webserver integrierten php.

Ja, das ist nicht hilfreich, aber es ist zu befürchten, daß das 
irgendjemand so begründen wird.

Aber das ist halt auch ein Grenzfall, nämlich die Nutzung der 
Betriebssystemschnittstelle des C-Runtime-Systems, und nicht die Nutzung 
von C an sich. Wenn Du also vermeidest, Funktionen zu verwenden, die 
stdin/stdout/stderr verwenden, müsstest Du zurechtkommen können.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Harald K. schrieb:
> Nun, das Beispiel funktioiert ja. Mit php.
> Nur nicht mit dem in Deinen Webserver integrierten php.
>
> Ja, das ist nicht hilfreich, aber es ist zu befürchten, daß das
> irgendjemand so begründen wird.
Wenns das tatsaechlich ist, dann isses voellig OK. Waere evtl. ein 
kleiner Hinweis bei dem offiziellen Beispiel nicht schlecht. Wenns aber 
prinzipiell funktioniert, nur bei mir - aus Gruenden - halt nicht, dann 
isses doof.

> Aber das ist halt auch ein Grenzfall, nämlich die Nutzung der
> Betriebssystemschnittstelle des C-Runtime-Systems, und nicht die Nutzung
> von C an sich.
Wie ich das das erste Mal mitgekriegt hab, dass es ueberhaupt sowas 
gibt, habbich auch erstmal 3..4 Baukloetze gestaunt.

Gruss
WK

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Damit ich's mal wieder find', wenn ich's brauch, und damit andere sich 
ordentlich amuesieren koennen, hier mal ein Beispiel, was ohne stdout 
auskommt und bei mir so tut, wie ich es erwartet habe:
bla.c:
1
/* compile with
2
3
gcc -Wall -Wextra -shared -fPIC -o libbla.so bla.c
4
5
*/
6
7
#include <string.h>
8
#include <stdlib.h>
9
#include <syslog.h>
10
struct sbla {
11
    int inumber;
12
    char *snumber;
13
};
14
15
struct sbla return5(void) {
16
    struct sbla retval;
17
    retval.inumber = 5;
18
    retval.snumber = strdup("pfuempf");
19
    syslog(LOG_INFO,"in return5()\n");
20
    return retval;
21
}
22
23
void free5(struct sbla *pbla) {
24
    syslog(LOG_INFO,"in free5()\n");
25
    if (pbla) {
26
        free(pbla->snumber);
27
    }
28
}

bla.php:
1
<?php
2
$ffi = FFI::cdef("
3
struct sbla {
4
    int inumber;
5
    char *snumber;
6
};
7
8
struct sbla return5(void);
9
void free5(struct sbla*);" , "libbla.so");
10
$retval = $ffi->return5(); // call libfunction
11
$retstr = ffi::string($retval->snumber);
12
$retint = $retval->inumber;
13
var_dump($retval);
14
$ffi->free5(ffi::addr($retval)); //call libfunction
15
var_dump($retstr);
16
var_dump($retint);
17
?>

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Ob man ffi::cdef auch eine *.h-Datei unterjubeln kann? Dann könnte 
struct sbla und die Prototypen dort untergebracht und sowohl vom 
C-Compiler als auch ffi verwendet werden, was die Fehlerquote bei 
Änderungen reduzieren würde (keine mehreren, aktuell zu haltenden 
identischen Deklarationen an unterschiedlichen Stellen)

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Harald K. schrieb:
> Ob man ffi::cdef auch eine *.h-Datei unterjubeln kann?

Ich glaub' schon. Das hier sieht danach aus:
https://www.php.net/manual/en/ffi.examples-complete.php
Nur bin ich halt dazu derzeit intellell noch nicht in der Lage ;-)

Gruss
WK

von Harald K. (kirnbichler)


Lesenswert?

Dergute W. schrieb:
> Nur bin ich halt dazu derzeit intellell noch nicht in der Lage

Ach, Du schaffst das. Erst mal 'nen geruhsamen ersten Advent.

von Εrnst B. (ernst)


Angehängte Dateien:

Lesenswert?

Generell: Du schreibst mittels C-printf direkt auf STDOUT in deinem 
PHP-Script.
Früher™, wenn PHP als CGI eingebunden war, ging das mit in die Antwort 
des Webservers ein.
War aber eher so eine Art dreckiger Nebeneffekt, dass das ging. Und hat 
auch nicht gut mit "output_buffering", "output_handler", "ob_start()&co" 
zusammengespielt.

mit FPM geht das garnicht mehr.
Die Default-Config von FPM hat catch_workers_output=no
Bedeutet: Alle deine STDOUT-Writes landen in /dev/null.
Wenn du das auf "yes" stellst, landen deine C-printfs im Logfile.
In der Webserver-Antwort landen sie nie.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Tja, so ist das halt. Wenn ich 12V für eine Fritzbox möchte und nur 
400V/125A habe, funktioniert ein Zwischenstecker mit einem einfachen 
Stück Kabel dran, auch eher suboptimal.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Ben B. schrieb:
> funktioniert ein Zwischenstecker mit einem einfachen
> Stück Kabel dran, auch eher suboptimal.

Der wird aber auch nicht ausdruecklich im Benutzerhandbuch der Fritzbox 
als Beispiel fuer eine Stromversorgung erwaehnt.

Εrnst B. schrieb:
> Die Default-Config von FPM hat catch_workers_output=no
> Bedeutet: Alle deine STDOUT-Writes landen in /dev/null.
> Wenn du das auf "yes" stellst, landen deine C-printfs im Logfile.

Wollt' ich spasshalber mal probieren, scheitert aber auch klaeglich. 
Mein php (8.2.9) erkennt diese Option weder in php.ini noch php-fpm.conf
Naja, eh' wurscht. Was ich ans laufen bringen wollte, laeuft soweit.

Gruss
WK

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.