数组接口的乐趣
As a programmer who works with different languages every day, I find a lot of joy in learning how things are done differently in other languages and seeing if I can do the same in PHP. One thing I liked in particular in Python was how one can emulate features of native data types in custom classes.
作为每天使用不同语言工作的程序员,我在学习如何用其他语言进行不同的处理以及查看是否可以在PHP中完成工作方面感到很高兴。 我特别喜欢Python的一件事是,如何在自定义类中模拟本机数据类型的功能。
Take for example this members list class:
以这个成员列表类为例:
class Members:
def __init__(self, members):
self.members = members
// other methods
By implementing the __iter__
method you could iterate over data in an instance of this class just as you would a list (array in PHP):
通过实现__iter__
方法,您可以像访问列表(PHP中的数组)一样遍历此类实例中的数据:
class Members:
def __iter__(self):
return self.members
ls = Members(["You", "Me"])
for member in members:
print members
Testing for membership would simply entail implementing the __contains__
method:
测试成员资格仅需要实现__contains__
方法:
class Members:
def __contains__(self, member):
return member in self.members
From there you could do:
从那里您可以执行以下操作:
"Me" in members:
print "I am a member!"
I thought it would be nice if you could do the same in PHP on an instance of your custom classes and not only arrays:
我认为如果可以在PHP中对自定义类的实例(而不只是数组)执行相同的操作,那将是很好的:
isset($myObject["test"]);
PHP lets us do this with array interfaces.
PHP让我们可以使用数组接口来做到这一点。
界面概述 (Interfaces in a nutshell)
Think of interfaces as contracts specifying methods that a class has to contain.
可以将接口视为指定类必须包含的方法的协定。
interface Multiplier {
public function multiply($num1, $num2);
}
Any class using this interface must have such a multiply method. There is a keyword to signify that a class fulfills this contract: implements.
使用此接口的任何类都必须具有这种乘法方法。 有一个关键字表示一个类满足此合同:实现。
class SmartMultiplier implements Multiplier {
public function multiply($num1, $num2) {
return $num1*$num2;
}
}
It doesn't matter how the contract is fulfilled as long as it is. An alternative way of implementing the multiply method can go like this:
只要履行合同,合同的履行方式都没有关系。 实现乘法方法的另一种方法可以像这样:
class NotSoSmartMultiplier implements Multiplier {
public function multiply($num1, $num2) {
$product = $num1;
foreach(range(1,$num2-1)) {
$product = $num + $num2;
}
return $product;
}
}
SPL和PHP接口 (SPL and PHP Interfaces)
PHP provides a library of predefined interfaces that can make our objects array-like by simply implementing them in the classes.
PHP提供了一个预定义接口的库,该接口可以通过简单地在类中实现它们来使我们的对象像数组一样。
Some of these interfaces are included in the list of Predefined Interfaces and Classes and some in Standard PHP Library (SPL).
其中一些接口包含在预定义的接口和类的列表中,另一些包含在标准PHP库 (SPL)中。
If those terms sound intimidating, don't let them. You've all used $_GET
before. $_GET
is a language construct that is said to be predefined.
如果这些术语听起来令人生畏,请不要让它们。 您以前都使用过$_GET
。 $_GET
是一种语言构造,据说是预定义的 。
On the other hand, from the documentation, SPL is just
另一方面,从文档来看,SPL只是
a collection of interfaces and classes that are meant to solve common problems.
旨在解决常见问题的接口和类的集合。
All that has to be done now is to see some of those interfaces in action. So let's dive in!
现在要做的就是查看其中的一些接口。 因此,让我们开始吧!
We are going to create a Twitter timeline class,
我们将创建一个Twitter时间轴类,
$tweets = new Timeline("jeunito");
able to count its tweets,
能够计算其推文
count($tweets);
loop through them,
遍历他们,
foreach($tweets as $tweet) {
echo $tweet;
}
and get a tweet via a tweet id,
并通过推特ID获得一条推特,
// get
if (isset($tweets["some tweet id"])) {
echo $tweets["some tweet id"];
}
just as we would in a normal array!
就像我们在普通阵列中一样!
We have to get some things out of the way though. First create a Twitter account if you don't have one yet. Now sign up for a developer account and generate an access token and secret.
但是,我们必须解决一些问题。 如果您还没有Twitter帐户,请先创建一个。 现在注册一个开发人员帐户并生成访问令牌和密码。
Next, download or clone the code from Github and run composer install
inside the source folder. If you're unfamiliar with Composer, see SitePoint's previous article on it. Open the index.php file and add in the necessary Oauth data.
接下来,从Github下载或克隆代码,然后在源文件夹中运行composer install
。 如果您不熟悉Composer,请参阅SitePoint的先前文章 。 打开index.php文件,并添加必要的Oauth数据。
可数接口 (The Countable Interface)
The Countable interface is probably the most self explanatory. It lets us pass objects to the count()
method simply by implementing the count method.
Countable接口可能是最能说明问题的。 它使我们可以简单地通过实现count方法将对象传递给count()
方法。
We can get the number of tweets for a user by doing a GET request on "/users/show".
通过在“ / users / show”上执行GET请求,我们可以获得用户的推文数量。
public function count()
{
$result = $this->api->get("users/show", array(
'screen_name' => $this->username
));
return $result['statuses_count'];
}
ArrayAccess接口 (The ArrayAccess Interface)
We are now going to up the ante a bit by learning about a more interesting interface.
现在,我们将通过学习一个更有趣的界面来稍微提高一下赌注。
When implemented, ArrayAccess
will enable our objects to be accessed like a map, which is really what they are. The methods to implement are
实施后, ArrayAccess
将使我们的对象像地图一样被访问,这确实是它们的ArrayAccess
。 实现的方法是
ArrayAccess {
abstract public boolean offsetExists ( mixed $offset )
abstract public mixed offsetGet ( mixed $offset )
abstract public void offsetSet ( mixed $offset , mixed $value )
abstract public void offsetUnset ( mixed $offset )
}
This is very handy in our Twitter timeline object. Testing if a tweet exists in a timeline would be done by passing our object to isset
like so:
这在我们的Twitter时间轴对象中非常方便。 测试时间轴中是否存在推文,可以通过将对象传递给isset
来完成,如下所示:
isset($tweets['some tweet id']);
To do that we simply perform a GET request on a tweet id.
为此,我们只需要在一个推特ID上执行GET请求即可。
public function offsetExists($offset) {
$tweet = $this->api->get("statuses/show", array("id" => $offset));
if ($tweet) {
return $tweet["text"];
}
return false;
}
Even better, we can also use the above for offsetGet
instead and let offsetExists
call offset Get in turn.
更好的是,我们也可以将上述内容用于offsetGet
然后让offsetExists
调用offset Get。
public function offsetGet($offset) {
$tweet = $this->api->get("statuses/show", array("id" => $offset));
if ($tweet) {
return $tweet["text"];
}
return false;
}
public function offsetExists($offset) {
return $this->offsetGet($offset) !== false;
}
With offsetUnset
we could also do a delete by tweet id, but I'll leave that up to you to implement.
使用offsetUnset
我们还可以通过tweet id进行删除,但是我将由您自己实现。
Unfortunately, implementing offsetSet does not make much sense. For things like this, the easy way out is to just throw a custom exception like UnsupportedOperationException
. But on the other hand, it may depend on your application's specific business rules as well.
不幸的是,实现offsetSet没有多大意义。 对于这样的事情,简单的解决方法是只抛出自定义异常,例如UnsupportedOperationException
。 但另一方面,它也可能取决于应用程序的特定业务规则。
迭代器接口 (The Iterator Interface)
I have reserved the interface I like the most for last! The Iterator interface is extremely useful here because I don't think there is a better way to encapsulate the gory details of paging through a remote collection than looping through our timeline object as if it were a normal array.
我保留了我最喜欢的界面! Iterator接口在这里非常有用,因为我认为没有比通过普通时间数组循环遍历时间轴对象更好的方法来封装通过远程集合进行分页的棘手细节了。
First, the following methods need to be implemented:
首先,需要实现以下方法:
Iterator extends Traversable {
/* Methods */
abstract public mixed current ( void )
abstract public scalar key ( void )
abstract public void next ( void )
abstract public void rewind ( void )
abstract public boolean valid ( void )
}
We could explicitly use methods above to loop through our timeline like so:
我们可以显式地使用上述方法遍历时间轴,如下所示:
$it->rewind();
while ($it->valid())
{
$key = $it->key();
$value = $it->current();
// do something
$it->next();
}
?>
But why do that when you can do this:
但是,为什么可以这样做呢?
foreach($tweets as $id => $tweet) {
echo $tweet;
}
In our example, we are going to loop through the tweets in our timeline by chronologically retrieving chunks of tweets and then storing them in a buffer. We will iterate through this buffer until it runs out and then we got another batch of tweets using the id of the last tweet as offset.
在我们的示例中,我们将通过按时间顺序检索大块tweet,然后将它们存储在缓冲区中来遍历时间轴中的tweet。 我们将循环访问此缓冲区,直到用完为止,然后使用最后一条推文的ID作为偏移量获得另一批推文。
Initially we have none and this is where the rewind method comes in: to get the latest 10 tweets so have an offset from where we can get the next 10.
最初我们没有,这就是rewind方法的用处:要获取最新的10条tweet,请与获取下10条tweet的位置偏移。
public function rewind()
{
$this->tweets = $this->api->get('statuses/user_timeline', array('count' => 20));
}
The valid()
method is just there to indicate whether or not to continue looping. This can be done by checking if our buffer of tweets is emtpy:
valid()
方法仅用于指示是否继续循环。 这可以通过检查我们的tweets缓冲区是否为空来完成:
public function valid()
{
return !empty($this->tweets);
}
The key()
and current()
methods simply return the key and value of the current tweet in our iteration. For our purposes, we will simply get the tweet id and the text of the latest tweet from our buffer.
key()
和current()
方法仅在迭代中返回当前tweet的键和值。 出于我们的目的,我们仅从缓冲区中获取tweet ID和最新tweet的文本。
public function key()
{
$latest = reset($this->tweets);
return $latest['id_str'];
}
public function current()
{
$latest = reset($this->tweets);
return $latest['text'];
}
Finally there is the next method. Here we dequeue the head of our buffer to get the next element to iterate on. Then, we ensure we get the next set of tweets if we are at the last element of our buffer.
最后是下一个方法。 在这里,我们使缓冲区的头部出队,以使下一个要迭代的元素成为可能。 然后,如果我们位于缓冲区的最后一个元素,则确保获得下一组推文。
public function next()
{
$head = array_shift($this->tweets);
if (!is_null($head) && empty($this->tweets))
{
$this->tweets = $this->api->get('statuses/user_timeline', array('count' => 800, 'max_id' => $head['id_str']));
}
}
We are done! That was a very basic implementation of looping through a user's tweets. There is a lot more that can be done like caching locally to save on api calls but that's the beauty of using interfaces: they allow us to change our strategy under the hood and as long as our implementation is still correct, we can expect it to still work.
我们完了! 那是在用户的推文中循环的非常基本的实现。 还有很多事情可以做,例如在本地缓存以节省api调用,但这就是使用接口的好处:它们使我们能够在后台更改策略,只要我们的实现仍然正确,我们就可以期望这样做仍在工作。
But for now, you can watch our timeline object working hard by running php index.php
at the command line.
但是就目前而言,您可以通过在命令行运行php index.php
来观察我们的时间轴对象的工作情况。
结论 (Conclusion)
The benefit of Interfaces is two-fold. They let us encapsulate implementation details and afford us syntactic sugar, both of which can be a real charm in any application that asks for interoperability. If you have any questions or comments, please leave them in the comments section below!
接口的好处是双重的。 它们让我们封装了实现细节,并为我们提供了语法糖,这两者在任何要求互操作性的应用程序中都可以成为真正的魅力。 如果您有任何问题或意见,请将其留在下面的评论部分!
上一篇: 网站建设中关于并发连接数的解释