Tag Archives: Zend-framework

Handling ZF2 FQ Dependency Setter Injections

In a previous article I showed how you could pass in a fully qualified parameter name into the Dependency Injection Container (DiC) if you needed to be specific about where you need to have something injected.  There is an alternate method here that is cleaner than what I did before.  Let’s start with a Test class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Test
{
  protected $test;
 
  public function setTest($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}

The way I did it in the previous example was by defining the fully qualified parameter name by doing this.

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'Test::setTest:0' => 'some data';
      )
    )
  )
);

Which is fine until you you have

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'FriggingLongNamespacedClass' => array( // Class name
      'parameters' => array(
        'FriggingLongNamespacedClass::setFriggingLongNamespacedClassMethod:0' => 'some data';
      )
    )
  )
);

It can get a little convoluted, plus I’m not entirely convinced that’s the best way of doing it.  Parameters seem to be more useful for doing constructor injection.  A better method for setters seems to be through the use of injectors.  Let’s take at an injector configuration.

1
2
3
4
5
6
7
8
9
10
11
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'injections' => array(
        'setTest' => array(
          array('test' => 'somedata')
        )
      )
    )
  )
);

Basically, rather an using the “parameter” key use “injections” and specify an array of injection methods.  Each of those methods needs an associative array of parameter names as the key with the values as the value.  Injectors seem to be much nicer and cleaner than my previous way of doing the fully qualified parameter.

ZF2 Dependency Injection – Multiple Object Instances

When you work with the ZF2 Dependency Injection Container (DiC) when you make multiple requests for an instance of an object you will get the same object back each time.

For example, with this code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Test
{
  protected $test;
 
  public function __construct($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}
 
use Zend\Di\Di;
$di = new Di();
 
$test = $di->get('Test', array('test' => 'some data'));
var_dump($test);
$test2 = $di->get('Test', array('test' => 'some data'));
var_dump($test2);

You will get the following output

1
2
3
4
5
6
7
8
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}

If you provide a different parameter you will get a different object.

1
2
3
4
$test = $di->get('Test', array('test' => 'some data'));
var_dump($test);
$test2 = $di->get('Test', array('test' => 'some other data'));
var_dump($test2);

Produces

1
2
3
4
5
6
7
8
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}
object(Test)#11 (1) {
["test":protected]=>
string(15) "some other data"
}

From this we can conclude that when you are using the DiC that if you request a like object with like parameters you will get the same instance of the object.

But what if you want the injection benefits of the DiC but don’t want to share the object?  Use the DiC’s newInstance method instead with the third parameter being false.  This tells the DiC container to refrain from putting it in the shared object pool.  The get() method does not give you the option of retrieving a non-shared instance and so you will need to call the newInstance() method directly.

1
2
3
4
5
$test = $di->get('Test', array('test' => 'some data'));
var_dump($test);
 
$test2 = $di->newInstance('Test', array('test' => 'some data'), false);
var_dump($test2);

prints out

1
2
3
4
5
6
7
8
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}
object(Test)#11 (1) {
["test":protected]=>
string(9) "some data"
}

 

ZF2 Dependency Injection: Managing Configuration – Part 2

In my previous post about Dependency Injection Container (DiC) configuration I made mention of some more complicated scenarios that you can use to configure your objects.  Not that you need help to make it  more complicated.  One of the things I have observed is that the more familiar I am with the DiC the more complicated the configuration gets.  This is both good and bad.  It’s good because you start understanding how and why all of those frigging long example DI configuration arrays are like that.  The bad is that  long DiC configuration arrays greatly reduce the ability of a new developer to start being productive.  That’s why I think this series of blog posts are good.  They show you how to get started small which is really the only way to get started if you aren’t intimately familiar with a DiC.

Let’s take our Test class and change its behavior by adding a method that allows you to set some test data (use your imagination here before complaining in the comments that it’s not a real world example).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Test
  {
  protected $test;
  protected $data;
 
  public function setTestData($test)
  {
    $this->data = $test;
  }
 
  public function getTestData()
  {
    return $this->data;
  }
 
  public function setTest($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}

We dutifully set up our DiC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'test' => 'some data'
      )
    )
  )
);
 
use Zend\Di\Configuration;
 
$diConfig = new Configuration($config);
$di = new Di();
$diConfig->configure($di);

And we run our test code getting our ‘some data’ parameter, right?

1
string(9) "some data"

Yep.  Hmm, but what about getTestData()?

1
var_dump($test->getTestData());

echos

1
string(9) "some data"

Hmm, that shouldn’t be happening.  We did’t tell the DiC that we wanted to inject data there, did we?  It looks like it’s injecting data into both setter methods.  But why?

Look at the names of the parameters for both setters.  They are both named “test”.  The DiC will find the first method with a matching parameter name and inject the value there (if it couldn’t find it in the constructor) .  You have a couple of options to fix this.  If it’s your own code and you don’t mind refactoring, refactor it.  However, if it’s third party code that you shouldn’t touch or code that other parts of the system are dependent on, you have a problem.

But to get around the problem you can specify the setter parameter as a fully qualified (FQ) parameter (I show another , probably better, method here).  The format is “class::method:paramPos”.  In this example it would be “Test::setTest:0″.  So let’s change our DI configuration.

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'Test::setTest:0' => 'some data'
      )
    )
  )
);

And now check the return values of both getters.

1
2
3
$test = $di->get('Test');
var_dump($test->getTest());
var_dump($test->getTestData());

Now we get

1
2
string(9) "some data"
NULL

Works.

ZF2 Dependency Injection: Managing Configuration – Part 1

Configuration is a big reason to use a Dependency Injection Container (DiC).  I’ve been doing a lot of playing around with the ZF2 DiC and one of the things that I like about it is the ability to retrieve fully configured objects in  one line of code.  I’ve been going a bit overboard lately but there’s a lot of stuff you can do that can just be downright complex to handle on your own, all the time.

In my previous blog post I showed how you could provide parameters to object that you’re pulling from a DiC and have them populated in the resulting object.  As cool as that is, it’s not a massive saving as you need to manually inject the parameters into the DiC.  You can often do the same thing by setting up the DiC ahead of time to get the object with those parameters pre-set.

Let’s start by defining a class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Test
{
  protected $test;
 
  public function __construct($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}

What we did before was use call time parameters to set the $test variable.

1
2
3
$di = new Di();
$test = $di->get('Test', array('test' => 'some data'));
echo $test->getTest();

Running this produces

1
'some data'

What if we either don’t know what ‘test’ will be, we want to minimize our code or we want to centralize our injection requirements?  For any of those we can set up the DiC to inject that data for us.

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'test' => 'some data'
      )
    )
  )
);

The structure of the configuration is this.  The first level is the key “instance”.  This will limit the objects created to a single instance in the DiC when requested with given set of parameters, similar to a registry (someone in the ZF team can correct me if I’m wrong).  The second level of the array is the key of the class name.  Our class is called “Test” and so we call the key “Test”.  If it was “Api\Service\Account” the key would be “Api\Service\Account”.  Next up are various options for the injection.  The only option I’m concerned with is the “parameters” one.  That is where you put in the various injections the object needs in a key/value array.

In the parameters array the keys will refer to the names of the variables you are injecting into the class.  The DiC will try to match it in two places.  The first is the named parameters in the constructor and the second are the variable names in the setters in the class.  The latter scenario may require some additional information, but we’ll look at that in a later blog post.

The first thing we need to do is take that configuration and provide it to a configuration object and then have the configuration object configure the DiC for us.

1
2
3
4
5
use Zend\Di\Configuration;
 
$diConfig = new Configuration($config);
$di = new Di();
$diConfig->configure($di);

We now have a configured DI object.  So to execute our code from earlier we change it just slightly.

1
2
$test = $di->get('Test');
echo $test->getTest();

The could echos

1
'some data'

This is now equivalent to our previous call.  Now this may seem all nice and well, but there’s more to it.  What if your object depends on an instance of another class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Test
{
  protected $test;
 
  public function __construct(Exam $test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}
 
class Exam
{
  protected $grade;
 
  public function __construct($grade)
  {
    $this->grade = $grade;
  }
 
  public function getGrade()
  {
    return $this->grade;
  }
}

We have two classes.  One called Test and one called Exam.  Test requires Exam and so we need to define that injection requirement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'test' => 'Exam'
      )
    ),
    'Exam' => array(
      'parameters' => array(
        'grade' => 'A+'
      )
    )
  )
);

We redefined our Test injection to require an instance of “Exam” and we added another instance definition for Exam which provides the grade.  Then we’ll change our code slightly.

1
2
3
$test = $di->get('Test');
echo get_class($test->getTest()) . "\n";
echo $test->getTest()->getGrade();

And when we run it we get

1
2
Exam
A+

With that you can now configure a DI Container and start providing references to various objects in and around your application.

There is more to talk about but we’ll leave this post at that.  Check back soon for more examples.

A little more advanced ZF2 EventManager usage

<note>I found out that this usage is going to be deprecated.  This feature will remain, but for the GA (and beta 4 most likely) this code may not work.  I will be updating this blog post when that happens</note>

If you look at my previous post on the ZF2 EventManager class you might be tempted to think that you are limited only to listening to events for local instances of the event manager.  But no, my friend, you would be wrong.

The event manager actually provides access to a sort of global event manager.  But it’s more than that.  This global event manager allows you to listen in on events for various targets.

So let’s say that you have an event manager for a given class in a given namespace.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace ns1 {
 
  use Zend\EventManager\EventManager;
 
  class cls1 {
 
    protected $events;
 
    public function __construct()
    {
      $this->events = new EventManager(__CLASS__);
    }
 
    public function execute()
    {
      $this->events->trigger('meesa.triggered', $this);
    }
  }
}

Take a look at the constructor.  What you are doing there is giving the event manager an identifier.  Identifiers are not needed, but if you want to allow other event manager instances to attach to its events an identifier will make it globally known.

Next up we will create a class that attaches to that event manager by calling the StaticEventManager class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace ns2 {
 
  use Zend\EventManager\Event,
  Zend\EventManager\StaticEventManager;
 
  class cls2 {
 
    public function listen(Event $e)
    {
      echo get_class($this) . ' has been called by ' . get_class($e->getTarget());
    }
  }
 
  StaticEventManager::getInstance()->attach('ns1\cls1', 'meesa.triggered', array(new cls2, 'listen'));
}

We’re not calling the class ns1\cls1 in this case, just attaching to its event.  When setting this up we attach to the identifier that we provided in the first class (often a class name), followed by the event name and them providing the  class and method name via a callback.  When the event is triggered that callback will be called and we will we will  print out the target class.

1
2
3
4
use ns1\cls1;
 
$cls = new cls1();
$cls->execute();

When we execute that code we get the following output.

ns2\cls2 has been called by ns1\cls1

So even though ns2\cls2 and ns1\cls1 had virtually no knowledge of each other (we did because we “set them up to succeed”), we were able to call code from across the codebase with great ease.

Zend Framework 2 Event Manager

Yes, I know I work for Zend and that means that I should automatically be familiar with everything the company does.  Especially when it comes to Zend Framework 2.  But I have to confess that while I’m most definitely watching it, I have not been able to work with it in any meaningful sense.

Until today.  I got to play with the Event Manager.  I did like the plugin functionality in ZF1, but it required some pretty static coding.  In some cases, like the front controller plugins, it makes more sense (though this way seems more desirable).  Of course there are frameworks out there that are more event based as a whole, but what if you’re more familiar with Zend Framework?  I am, and so it makes sense that I would use the event manager in ZF2.  It’s a ZF1 application, but since (it seems) the event manager is self-contained (and the autoloader works with both ZF1 and ZF2) you can simply paste it into your include_path and BOOM, you have an event manager.

So, I had a model (the software kind) layer where all of the models were based off of an abstract class.  The abstract class had a save() method that I wanted to provide various hooks for pre and post save and, more importantly, pre and post validation.  Why validation?  Because I want to stored a hashed password in the database, but I want to validate on the plaintext.  I could have created a bunch of hooks, but that really is annoying.  I could have also overridden the save() method, but that meant adding a fair amount of logic to that method and I’m becoming less and less a fan of overriding logic because it’s insufficient at higher levels of abstraction.  So, instead, I implemented the ZF2 event manager in a ZF1 application.

The model looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
abstract class ModelAbstract
{
 
	const EVENT_PRESAVE = 'modelabstract-pre-save';
	const EVENT_PREVALIDATE = 'modelabstract-pre-validate';
	const EVENT_POSTVALIDATE = 'modelabstract-post-validate';
	const EVENT_POSTSAVE = 'modelabstract-post-save';
 
	/**
	 * @var EventManager
	 */
	protected $eventManager;
 
	public final function __construct()
	{
		$this->eventManager = new EventManager();
		$this->init();
	}
 
	public function init() {}
 
	public function save()
	{
		if ($this->tableName === null) {
			throw new ModelException('Improperly defined model');
		}
		$this->eventManager->trigger(self::EVENT_PREVALIDATE, $this);
		if (!$this->getForm()->isValid($this->data)) {
			return false;
		}
		$this->eventManager->trigger(self::EVENT_POSTVALIDATE, $this);
		$db = \Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('db');
		$this->eventManager->trigger(self::EVENT_PRESAVE, $this);
 
// save the data (redacted due to boredom)
 
		$this->eventManager->trigger(self::EVENT_POSTSAVE, $this);
		return $ret;
	}
}

Then in my Account model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Application_Model_Account extends ModelAbstract
{
	protected $tmpPassword;
 
	public function init()
	{
		// Encrypt the password after validation if the password has changed
		$this->eventManager->attach(
			self::EVENT_POSTVALIDATE,
			function($event) {
				if ($event->getTarget()->passwordChanged()) {
					$event->getTarget()->encryptPassword();
				}
			}
		);
	}
 
	public function passwordChanged() // really annoying. Thanks PHP 5.3
	{
		return $this->tmpPassword == null;
	}
 
	public function encryptPassword()
	{
		if ($this->passwordChanged()) {
			$util = new Util();
			$this->password = $util->encryptPassword($password);
			$this->tmpPassword = null;
		}
	}
}

If the password is changed it sets both $this->password and $this->tmpPassword.  The hashing only occurs if the password has been set and there’s a value in tmpPassword.  (I guess I could have just used a boolean somewhere too).  So now, every time I save the account model the post validate event will be fired and the password will be encrypted.  (The comment on PHP 5.3 is because you cannot use $this in a closure in PHP 5.3.  That functionality will be there in 5.4.)

I believe that the method I used here allows for events to be fired for specific object instances, because each model has its own event manager.  But the event manager will also check for global events as well.  So, technically, the code that I have that does validation could be registered as a global observer to make sure that all models are properly validated prior to the save as well as having instance-specific (such as password changes) events attached to the same event name.

Pretty cool.

… waiting for Matthew to email me with corrections… :-)

[UPDATE]

For those who have complained that I didn’t use some additional kind of methodology or best practice know that the other five billion lines of code that had nothing whatsoever to do with the event manager has been removed… or read my response down below on “Methodology Jackassery”.

Authentication using Zend_Amf

I forget why, but a few days ago I started doing some digging around with authentication in Zend_Amf_Server.  I had figured that I would add an adapter to the Zend_Amf_Server::setAuth() method and that would be it.

But I was wrong.

AMF allows for multiple request bodies to be sent at the same time.  Of those there are several “special” types of commands.  One of those commands is logging in.  What this means is that you don’t need a method that logs someone in for you.  Zend_Amf_Server handles authentication separately from your service classes.

Authentication for Zend_Amf_Server will generally use a combination of Zend_Auth and Zend_Acl components.  Zend_Auth is used to provide the credential verification while Zend_Acl is used to validate that the current user user can access the requested service method.  It is actually a relatively trivial task to restrict access to non-logged in users using the method that I will describe here.

The first step in the process is to create an authentication adapter.  It really doesn’t matter what you’re using.  What matters is that the adapter returns an identity object with a property called “role”.  The built in ACL handle expects this to be part of the identity object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Auth extends Zend_Amf_Auth_Abstract
{
	const LOGGEDIN_ROLE = 'loggedin';
 
	public function authenticate()
	{
		$identity = new stdClass();
		$result = Zend_Auth_Result::FAILURE;
 
		// Do a proper login, y'all
		if ($this->_username == 'test' && $this->_password == 'test') {
			$identity->role = self::LOGGEDIN_ROLE;
			$result = Zend_Auth_Result::SUCCESS;
		} else {
			$identity->role = Zend_Amf_Constants::GUEST_ROLE;
		}
 
		return new Zend_Auth_Result($result, $identity);
	}
}

The Auth class extends Zend_Amf_Auth_Abstract because Flex seems to require username and passwords as being the only mechanism for passing credentials.  The abstract class defines a method that hooks in with the special commands and passes the special credentials to the special adapter.  Clearly your authentication mechanism should be better than the one that I put in here, but you’ll get the idea.  The most important part is adding the role property to the identity object and passing it to the Zend_Auth_Result object.

Then in your gateway you need to add this adapter as well as create an simple ACL.

1
2
3
4
5
6
7
8
9
10
11
12
$server = new Zend_Amf_Server();
$server->addDirectory(realpath(__DIR__.'/../services'));
 
$acl = new Zend_Acl();
$acl->addRole(Auth::LOGGEDIN_ROLE);
$acl->allow(Auth::LOGGEDIN_ROLE);
$server->setAcl($acl);
 
$auth = new Auth();
$server->setAuth($auth);
 
echo $server->handle();

This adds the new Auth role to the ACL and says that it has access to everything.  Since there is no place where I allow guest access (denoted by Zend_Amf_Constants::GUEST_ROLE in the adapter) guest requests will be denied.

With just this little bit of code you now have a mechanism that will provide restricted access to all of your service objects.

SimpleCloud Part 5 – SimpleDB

I started this series back in December.  In fact I wrote 3 or 4 blog posts the day before I took two weeks of vacation.  It’s now approaching the end of the next quarter so I figured I should actually make some progress on this.

The last posting dealt with the concept of storage in the cloud.  In this one we are going to talk about database access.  You have probably heard about document databases.  While RDBMS systems are awesome for when you have related data and need ACID compliance, they are hard to scale.  When I was a consultant I was onsite with a customer who had a large Oracle implementation with some performance issues and had an Oracle consultant there at the same time.  The Oracle consultant was flabbergasted that I could get done in a week whereas their analysis could take several weeks to months.  The nature of a relational database dictates that it will require a LOT of logic, horsepower and consultant dollars to handle larger-scale scalability.

So, accessing data in a scalable environment will generally be easier (possible?) if you use non-relational data.  Well.. not NON relational, just not enforcing those relations in the same way an ACID compliant RDMS would.  So a document database makes a lot of sense and Amazon’s SimpleDB fits the bill nicely.  If you’re on EC2 it really makes the most sense, unless you need immediate consistency of data.  One of the ways you make data access scalable/highly available is by having many, many machines that can provide access to that data.  But that takes time to propagate that data to those machines and, like with the relation database I was talking about earlier, if you need immediate consistency across those nodes you need a lot of logic, horsepower with a bit of luck that you don’t accidentally deadlock the whole thing.  It’s just not worth it.  SimpleDB has what’s called Eventual Consistency”.  In other words, when you update, insert or delete eventually (within 2 seconds according to AWS, I think) the data will be righted.  Most of the time you can stand having data out of date for a little bit.

We will create our configuration just like we did with the storage adapter.

1
2
3
cloud.document_adapter="Zend_Cloud_DocumentService_Adapter_SimpleDb"
cloud.aws_accesskey="XXXXXXXXXXXXXXXXXXX"
cloud.aws_secretkey="XXXXXXXXXXXXXXXXXXX"

And when we want to get our document adapter we do just as we did before

1
2
3
4
5
6
7
8
$config = new \Zend_Config_Ini(__DIR__.'/../config/config.ini');
 
\Zend_Registry::set(
    'DocumentAdapter',
    Zend_Cloud_DocumentService_Factory::getAdapter(
        $config->cloud
    )
);

Now that we have our document adapter in the registry we can work with it.  I used it in two different places.  First, in the job itself so that the job would be able insert the references to the completed images so you can query them later on.  Second, when we query them later on.

The code in the asynchronous job is

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$documentAdapter = \Zend_Registry::get('DocumentAdapter');
$docClass = $documentAdapter->getDocumentClass();
$doc = new $docClass(
  array(
    'filename'    => $fileName,
    'name'        => $this->_name,
    'height'    => $height,
    'width'        => $width,
    'size'        => filesize($tmpfname),
    'date'        => date('c')
  ),
  $this->_sourceId . '_' . $width
);
 
$documentAdapter->insertDocument("images", $doc);

What this does is ask the document adapter for the document class (just in case there are some adapter-specific pieces of functionality) and creates the new document, inserting it into the DB.  When creating a new document object the first parameter of the constructor is a name=>value pair of the data you want to store and the second parameter is the optional primary key for the data.  When you insert the document you need to specify a collection for the document to be inserted into, images, in this case which is followed by the actual document object.

When querying the collection we do so by simply… well… querying the collection.

1
2
3
4
5
$session = new \Zend_Session_Namespace('ProcTask');
$adapter = \Zend_Registry::get('DocumentAdapter');
$query = $adapter->select();
$query->where('name = ?', array($session->name))->from("images");
$results = $adapter->query('images', $query);

Notice a few things.  First we’re not creating our select object directly, we’re asking the adapter for it.  Just like with the document object, the select object may have some adapter-specific logic.  Actually that’s quite likely.  Then you provide your query parameters, which can be done in a prepared statement-like syntax.  Before passing the query object to the adapter, you must provide the collection name to the query object.  Then, to get your data you need to pass in the collection name along with the query.  Why do you need to do that for both the query object and the adapter?  I dunno.  Maybe it’s a bug, or maybe it’s a feature.  I haven’t looked.

Once you have your data you can simply iterate over it and read each member like you would a stdClass object.

1
2
3
4
5
6
foreach ($results as $result):
  echo $result->height;
  echo $result->width;
  echo $result->size;
  echo $result->date;
}

Done

A really quick, no API, Zend_Service_Twitter example

One of the cool things in Zend Framework is the Zend_Service layer.  What it basically does is provide access to a variety of different service-based… well, services, so that you can easily integrate your application with other services.  One example is, of course, Twitter.  Often, to connect to various services you need to have an API key that you use to connect.  The same is true for Twitter, except for public feed information.  What this means is that you can search Twitter for free, with no API, easily.

I will assume that you are using the Autoloader.  Pretty much all of my scripts start with this.

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);

So, to do a quick, API-free Twitter search:

$jobs = $notAnnoying = 0;
$ts = new Zend_Service_Twitter_Search('json');
$sr = $ts->search('#php');
foreach (array_shift($sr) as $res) {

if (strpos($res[‘text’], ‘#job’) === false
&& strpos($res[‘text’], ‘#free’) === false) {
$notAnnoying++;
echo ‘:) ‘;
} else {
$jobs++;
echo ‘:( ‘;
}

echo $res[‘text’] . “<Br />”;
}
echo “<br />”;
echo “Not Annoying: {$notAnnoying}<br />”;
echo “Jobs Posting: {$jobs}<br />”;

The output for this is

 

:) finally, #ZCE exam scheduled for Fri, 5 Mar 2010 at 10:00am #zend #php
:) #php User Manager updated, fixing email issues; http://bit.ly/cId3Uc
:( We're hiring. Full-time PHP Developer. You got the goods? - http://twurl.nl/6of5oj - #job #hiring #php #webdev #fb
:( We're hiring. Full-time PHP Developer. You got the goods? - http://twurl.nl/6of5oj - #job #hiring #php #webdev #fb
:) Suche einen #Programmierer, der sich mit #HTML, #PHP und #Ajax auskennt (2010-03-23 Berlin) http://bit.ly/d0aZAw
:) #php Surviving Web site traffic peaks. http://tinyurl.com/yf7qhc7
:) $bool = (0 == 'aaa'); // これがtrueになるってどういう仕様だよ #php
:( Looking to hire 2 Web Developers for #Wordpress projects, #PHP http://bit.ly/asWaRx & #CSS http://bit.ly/c5WA71 #jobs call/text 858 254 9711
:) http://guwi.com.au We're now available for freelance webdesign and development work. #css #php #javascript
:( #php #freelance Rotating Banner PHP Script by shetlandpony: Recently I hired a person to upgrade my Invision forum... http://bit.ly/9Cly9z
:( #php #freelance File Sharing Script by kusoftware: I'm looking for a script where people can upload files and shar... http://bit.ly/aopLRz
:( #php #freelance Drupal site - Corrections and items needed by nearsource: We have a Drupal site from a client. ... http://bit.ly/ctkX3F
:( #php #freelance Nonpublic project #620248 by fraserbsquared: The project is Nonpublic. Description can be read onl... http://bit.ly/di26q0
:( #php #freelance Scrape website stats 2 with myboot by koolego2: *This is a private invite project only* Require... http://bit.ly/aLRrK5
:( #php #freelance Rotating Banner PHP Script by shetlandpony: Recently I hired a person to upgrade my Invision forum... http://bit.ly/9Cly9z

Not Annoying: 6
Jobs Posting: 9

Easy

Web Analytics