Ist es möglich, eine Methode in PHP als statisch und nicht statisch zu deklarieren?

70

Kann ich eine Methode in einem Objekt sowohl als statische als auch als nicht statische Methode mit demselben Namen deklarieren, der die statische Methode aufruft?

Ich möchte eine Klasse erstellen, die eine statische Methode "send" und eine nicht statische Methode hat, die die statische Funktion aufruft. Zum Beispiel:

class test {
    private $text;
    public static function instance() {
        return new test();
    }

    public function setText($text) {
        $this->text = $text;
        return $this;
    }

    public function send() {
        self::send($this->text);
    }

    public static function send($text) {
        // send something
    }
}

Ich möchte in der Lage sein, die Funktion auf diesen beiden aufzurufen

test::send("Hello World!");

und

test::instance()->setText("Hello World")->send();

ist es möglich?

Alphanyx
quelle
5
Warum sollten Sie das tun wollen, wenn ich fragen darf?
PeeHaa
47
Viele Leute möchten dies als schlechte Frage abtun, nur weil sie sich persönlich keine Verwendung dafür vorstellen können. Hier ein Beispiel: Ich brauchte dies, weil ich einige Funktionen für die Ereignisbehandlung bereitstellen wollte. Ich wollte SomeClass :: on ('some_event', $ handler_fn) anbieten, das auf jede Instanz von SomeClass angewendet wird, und auch $ instance-> on ('some_event', $ handler_fn), das nur für die bestimmte Instanz gilt. Also, gute Frage und danke an @lonesomeday für die richtige Antwort ohne unwissende Urteile.
Daniel Howard
1
Dekorateure!
Timo Huovinen
4
Viele Leute denken, nur PHP, vielleicht wäre es von Vorteil, einige andere Sprachen wie Ruby und Python auszuprobieren!
g13013

Antworten:

88

Sie können dies tun, aber es ist ein bisschen schwierig. Sie müssen es mit Überladung tun: die __callund__callStatic magische Methoden.

class test {
    private $text;
    public static function instance() {
        return new test();
    }

    public function setText($text) {
        $this->text = $text;
        return $this;
    }

    public function sendObject() {
        self::send($this->text);
    }

    public static function sendText($text) {
        // send something
    }

    public function __call($name, $arguments) {
        if ($name === 'send') {
            call_user_func(array($this, 'sendObject'));
        }
    }

    public static function __callStatic($name, $arguments) {
        if ($name === 'send') {
            call_user_func(array('test', 'sendText'), $arguments[0]);
        }
    }
}

Dies ist keine ideale Lösung, da es schwieriger ist, Ihrem Code zu folgen, aber es funktioniert, vorausgesetzt, Sie haben PHP> = 5.3.

einsamer Tag
quelle
33
Das macht meine Augen b̢̗̫͕l͓̫͈e҉͍̖͙ḙ̣̭̦̫̞͟d̼. Ich werde es nicht ablehnen, weil Sie ihn gewarnt haben, dass diese Art der Codierung sein Leben nicht erleichtern wird, und es ist hilfreich. Aber immer noch: <
Madaras Geist
11
@lonesomeday Up Voting, nur weil Sie die Geduld haben, den Code zu schreiben, der so gut wie keinen greifbaren Nutzen hat! noch beantwortet die Frage :-)
Nicholas King
4
@ Wahrheit Ja. Es sollte lauten: "Sie können dies tun, aber Sie sollten es wirklich nicht tun ."
einsamer
danke für die antwort .. es ist wahr, dass diese lösung für eine andere person nicht einfach zu befolgen ist .. also werde ich sie nicht verwenden .. aber es ist die lösung für mein problem also vielen dank
alphanyx
1
Sie alle sagen, es ist eine schlechte Idee, aber wäre es nicht nützlich und einfacher, so etwas zu tun, wenn Sie eine Funktion haben, die Sie als Mitglied verwenden möchten, um Zugriff auf die Mitgliedsvariablen zu erhalten, und eine andere mit den gleichen Zweck, aber in dem Sie Ihre eigenen Argumente verwenden könnten? Sicher, Sie könnten einfach die Instanz der Funktion verwenden, um die Variablen abzurufen, aber stellen Sie sich vor, sie wären privat. Es sollte zum Beispiel für Mathematik nützlich sein, es würde in Situationen von Nutzen sein, in denen eine Instanz der Klasse nicht benötigt wurde und in einer. Keine Verwirrung und schnellere Codierung. Ansonsten könnte man Kommentare verwenden. : D
TrisT
2

Nein, Sie können nicht zwei Methoden mit demselben Namen haben. Sie können im Grunde das Gleiche tun, indem Sie eine der Methoden umbenennen. Umbenennen test::send("Hello World!");in test::sendMessage("Hello World!");würde funktionieren. Ich würde nur eine einzelne Sendemethode mit einem optionalen Textargument erstellen, das die Funktionsweise der Methode ändert.

public function send($text = false) {
    if (!$text) {
        $text = $this -> text;
    }

    // Send something
}

Ich bin gespannt, warum Sie die statische Funktion überhaupt brauchen.

Jordan Shute
quelle
3
Das Umbenennen der statischen Methode ist definitiv der sinnvolle Weg, dies zu tun.
einsamer
2

Ich würde eine versteckte Klasse als Konstruktor erstellen und diese versteckte Klasse innerhalb der übergeordneten Klasse zurückgeben, deren statische Methoden den Methoden der versteckten Klasse entsprechen:

// Parent class

class Hook {

    protected static $hooks = [];

    public function __construct() {
        return new __Hook();
    }

    public static function on($event, $fn) {
        self::$hooks[$event][] = $fn;
    }

}


// Hidden class

class __Hook {

    protected $hooks = [];

    public function on($event, $fn) {
        $this->hooks[$event][] = $fn;
    }

}

Um es statisch zu nennen:

Hook::on("click", function() {});

Um es dynamisch zu nennen:

$hook = new Hook;
$hook->on("click", function() {});
Taufik Nurrohman
quelle
2
Ihre versteckte Klasse hat absolut keine Auswirkung, sie ruft immer die statische Methode auf. (Getestet in PHP-Version 5.3.0 - 7.4.1) @see: 3v4l.org/HIBKR
Radon8472
0

Ich bin damit einverstanden, dass dies um jeden Preis vermieden werden sollte, aber es gibt einige Fälle, in denen es nützlich sein könnte.

In den meisten Fällen wird Ihr Code nur unlesbar und nicht verwaltbar.

Glauben Sie mir, ich bin diesen Weg gegangen.

Hier ist ein Beispiel mit einem Anwendungsszenario, in dem es möglicherweise noch praktikabel ist.

Ich erweitere die Dateiklasse von CakePHP 3.0 als meine Standardklasse für die Dateiverwaltung.

Ich wollte einen statischen Mime-Guesser einsetzen.

In einigen Fällen habe ich einen Dateinamen anstelle einer tatsächlichen Datei, und in diesem Fall müssen einige Annahmen getroffen werden. (Wenn die Datei vorhanden ist, versuchen Sie, die MIME daraus abzurufen. Andernfalls verwenden Sie die Erweiterung des angegebenen Dateinamens.)

In anderen Fällen sollte die Standardmethode mime () funktionieren, wenn ich ein Objekt tatsächlich instanziiert habe. Wenn dies jedoch fehlschlägt, muss der Dateiname aus dem Objekt extrahiert und stattdessen die statische Methode aufgerufen werden.

Um Verwirrung zu vermeiden, war es mein Ziel, den MIME-Typ durch Aufrufen derselben Methode zu ermitteln:

Statisch:

NS\File::type('path/to/file.txt')

Als Objekt

$f = new NS\File('path/to/file.txt');
$f->type();

Hier ist mein Beispiel für eine erweiterte Klasse:

<?php

namespace NS;

class File extends \Cake\Utility\File
{

    public function __call($method, $args) {
        return call_user_func_array([get_called_class(), 'obj'.ucfirst($method)], $args);
    }
    public static function __callStatic($method, $args) {
        return call_user_func_array([get_called_class(), 'static'.ucfirst($method)], $args);
    }

    public function objType($filename=null){
        $mime = false;
        if(!$filename){
            $mime = $this->mime();
            $filename = $this->path;
        }
        if(!$mime){
            $mime = static::getMime($filename);
        }
        return $mime;
    }

    public static function staticType($filename=null){
        return static::getMime($filename);
    }

    public static function getMime($filename = null)
    {
        $mimes = [
            'txt' => 'text/plain',
            'htm' => 'text/html',
            'html' => 'text/html',
            'php' => 'text/html',
            'ctp' => 'text/html',
            'twig' => 'text/html',
            'css' => 'text/css',
            'js' => 'application/javascript',
            'json' => 'application/json',
            'xml' => 'application/xml',
            'swf' => 'application/x-shockwave-flash',
            'flv' => 'video/x-flv',
            // images
            'png' => 'image/png',
            'jpe' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'jpg' => 'image/jpeg',
            'gif' => 'image/gif',
            'bmp' => 'image/bmp',
            'ico' => 'image/vnd.microsoft.icon',
            'tiff' => 'image/tiff',
            'tif' => 'image/tiff',
            'svg' => 'image/svg+xml',
            'svgz' => 'image/svg+xml',
            // archives
            'zip' => 'application/zip',
            'rar' => 'application/x-rar-compressed',
            'exe' => 'application/x-msdownload',
            'msi' => 'application/x-msdownload',
            'cab' => 'application/vnd.ms-cab-compressed',
            // audio/video
            'mp3' => 'audio/mpeg',
            'qt' => 'video/quicktime',
            'mov' => 'video/quicktime',
            // adobe
            'pdf' => 'application/pdf',
            'psd' => 'image/vnd.adobe.photoshop',
            'ai' => 'application/postscript',
            'eps' => 'application/postscript',
            'ps' => 'application/postscript',
            // ms office
            'doc' => 'application/msword',
            'rtf' => 'application/rtf',
            'xls' => 'application/vnd.ms-excel',
            'ppt' => 'application/vnd.ms-powerpoint',
            // open office
            'odt' => 'application/vnd.oasis.opendocument.text',
            'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
        ];
        $e = explode('.', $filename);
        $ext = strtolower(array_pop($e));
        if (array_key_exists($ext, $mimes)) {
            $mime = $mimes[$ext];
        } elseif (function_exists('finfo_open') && is_file($filename)) {
            $finfo = finfo_open(FILEINFO_MIME);
            $mime = finfo_file($finfo, $filename);
            finfo_close($finfo);
        } else {
            $mime = 'application/octet-stream';
        }
        return $mime;
    }
}
Dieter Gribnitz
quelle
-1

Es tut mir leid, dass ich einen alten Thread gestoßen habe, aber ich möchte die Antwort von @lonesomeday erweitern. (Danke @lonesomeday für das erste Codebeispiel.)

Ich habe auch damit experimentiert, wollte aber die Methoden nicht so aufrufen, wie er sie im ursprünglichen Beitrag genannt hat. Stattdessen habe ich folgendes, was zu funktionieren scheint :

    class Emailer {

    private $recipient;

    public function to( $recipient )
    {
        $this->recipient = $recipient;
        return $this;
    }

    public function sendNonStatic()
    {
        self::mailer( $this->recipient );
    }

    public static function sendStatic( $recipient )
    {
        self::mailer( $recipient );
    }

    public function __call( $name, $arguments )
    {
        if ( $name === 'send' ) {
            call_user_func( array( $this, 'sendNonStatic' ) );
        }
    }

    public static function mailer( $recipient )
    {
        // send()
        echo $recipient . '<br>';
    }

    public static function __callStatic( $name, $arguments )
    {
        if ( $name === 'send' ) {
            call_user_func( array( 'Emailer', 'sendStatic' ), $arguments[0] );
        }
    }
}

Emailer::send( '[email protected]' );

$Emailer = new Emailer;
$Emailer->to( '[email protected]' );
$Emailer->send();
Spund
quelle