PHP, ffi Beispiele

OP #7548845
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

OP #7548994
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

OP #7549015
Lesenswert?

Moin, Harald K. schrieb im Beitrag #7549002:

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

#7549027
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.

OP #7549034
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

#7549049
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.

#7549081
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.

OP #7549272
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

OP #7549592
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

#7549634
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)

#7550600
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.

Angehängte Dateien:
OP #7550737
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren