有没有可能在 PHP 中声明一个静态和非静态的方法?

对象中的方法是否可以同时声明为静态和非静态方法,其名称与调用静态方法的名称相同?

我想创建一个类,它有一个静态方法“ send”和一个调用静态函数的非静态方法。例如:

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
}
}

我希望能够调用这两个函数

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

还有

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

有可能吗?

23878 次浏览

You can do this, but it's a bit tricky. You have to do it with overloading: the ABC0 and __callStatic magic methods.

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]);
}
}
}

This isn't an ideal solution, as it makes your code harder to follow, but it will work, provided you have PHP >= 5.3.

No you can't have two methods with the same name. You could do basicly the same thing by renaming one of the methods. Renaming test::send("Hello World!"); to test::sendMessage("Hello World!"); would work. I would just create the a single send method with an optional text argument that changes how the method functions.

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


// Send something
}

I courious as to why you need the static function at all.

Sorry for bumping an old thread, but I would like to expand on @lonesomeday 's answer. (Thanks @lonesomeday for the initial code sample.)

I was also experimenting with this as well, but did not want to call the methods as he called them in the original post. Instead I have the following, which seems to work:

    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( 'foo@foo.foo' );


$Emailer = new Emailer;
$Emailer->to( 'bar@bar.bar' );
$Emailer->send();

I agree that this should be avoided at all costs but there are some cases where it might be useful.

In most cases it will just make your code unreadable and unmanageable.

Believe me, I have been down that path.

Here is an example with a use case scenario where it might still be practical.

I am extending CakePHP 3.0's File class as my default file handling class.

I wanted a to put in a static mime type guesser.

In some cases I have a filename instead of an actual file and some assumptions need to be made in this case. ( if the file exists, try to get the mime from it else use extention of filename provided)

Other times if I actually instantiated an object the default mime() method should work but if it fails the filename needs to be extracted from the object and the static method should be called instead.

To avoid confusion my aim was to get the mime type by calling the same method:

Static:

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

As object

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

Here is my example extended class:

<?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;
}
}

I would make a hidden class as the constructor and return that hidden class inside the parent class that has static methods equal to the hidden class methods:

// 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;
}


}

To call it statically:

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

To call it dynamically:

$hook = new Hook;
$hook->on("click", function() {});

In php, you can set/asign a class method with method visibility (Public, Private, Protected) also class properties, which can declare the restric distribution of class method or class properties, like those can access outside of the class or not..

And for calling purpose we get two approch,

  1. Static (self::)
  2. Non Static ($this->)

Lets take a class Foo have some Methods And Properties.. Which have visibilites and calling approch

    <?php


class Foo {


public const WELCOME ='This is WELCOME Non Static Constant For Foo Class';
public string $text='This is A Text Non Static Foo Class Properties';
public static string $texter='This is A Texter Foo Static Class Properties';
private string $ptext='This is a private string Non Static properties of Class Foo';
      

    

public static function Bar()
{
echo "Static Method Bar is calling\n";
}
    

public function Baz()
{
echo "Non Static Method Baz is calling \n";
}
    

    

protected function Another()
{
echo "Non Static Method Another is calling \n";
}
    

private function Again()
{
echo "Non Static Private Method Again is calling \n";
}
    

protected static function AnotherOne()
{
echo "Non Static Method Another is calling \n";
}
    

private static function AgainOne()
{
echo "Non Static Private Method Again is calling \n";
}
    

    

public static function bypass()
{
return self::AgainOne();
}
    

public function getPText()
{
return $this->ptext;
}
    

    

    

}
?>

Now Test This Class

<?php


//Non Static Call By Creating an $app instance of Foo Class..
$app = new Foo();
echo $app->WELCOME;        // Undefined property: Foo::$WELCOME
echo $app->text;           // This is A Text Non Static Foo Class Properties
echo $app->texter;         // Accessing static property Foo::$texter as non static
echo $app->Bar();          // Static Method Bar is calling
echo $app->Baz();          // Non Static Method Baz is calling
echo $app->Another();      // Uncaught Error: Call to protected method Foo::Another() from global scope
echo $app->Again();        // Uncaught Error: Call to private method Foo::Again() from global scope
echo $app->AnotherOne();   // Uncaught Error: Call to protected method Foo::AnotherOne() from global scope
echo $app->AgainOne();     // Uncaught Error: Call to private method Foo::AgainOne() from global scope
echo $app->bypass();       // Non Static Private Method Again is calling
echo $app->ptext;          // Uncaught Error: Cannot access private property Foo::$ptext
echo $app->getPText();     // This is a private string Non Static properties of Class Foo


//Static Call
echo Foo::WELCOME;         // This is WELCOME Non Static Constant For Foo Class
echo Foo::text;            // Uncaught Error: Undefined constant Foo::text
echo Foo::texter;          // Uncaught Error: Undefined constant Foo::texter
echo Foo::Bar();           // Static Method Bar is calling
echo Foo::Baz();           // Uncaught Error: Non-static method Foo::Baz() cannot be called statically
echo Foo::Another();       // Uncaught Error: Call to protected method Foo::Another() from global scope
echo Foo::Again();         // Uncaught Error: Call to private method Foo::Again() from global scope
echo Foo::AnotherOne();    // Uncaught Error: Call to protected method Foo::AnotherOne() from global scope
echo Foo::AgainOne();      // Uncaught Error: Call to private method Foo::AgainOne() from global scope
echo Foo::bypass();        // Non Static Private Method Again is calling
 

?>


See In Action here.