Saturday, August 25. 2012
With the release of vfsstream 1.1.0 a whole range of new features is introduced. First and foremost is support for new features which became possible with PHP 5.4, namely that 5.4 enables support for touch(), chown(), chgrp() and chmod() on stream wrappers. vfsStream 1.1.0 now supports these functions, which means they can be applied to vfsStream urls. This allows to test functions and methods which change file permissions or ownership.
Another new feature that came along with PHP 5.4 is support for truncating stream wrapper streams via ftruncate(). Of course, vfsStream now supports this as well.
Despite support for the new PHP 5.4 features vfsStream 1.1.0 can still be used with PHP 5.3, but of course without the features requiring PHP 5.4.
Last but not least vfsStream 1.1.0 introduces a new quota feature. This allows to limit the available space in the virtual file system and allows to simulate how your code behaves in case you run out of disk space. For more details check the documentation.
As there was no release announcement for 1.0.0, here is what changed with 1.0.0: vfsStream was changed to make use of namespaces, which means support for PHP 5.2 was dropped. All vfsStream classes now reside in org\bovigo\vfs. (Yeah, it is. Maybe I'm spoiled by Java here. ) Besides that, the release contains some bug fixes. See changelog for a complete list of changes.
Additionally, since 1.0.0 vfsStream is no longer available via PEAR, but via Composer. You can get releases via Packagist.
As the old PEAR server on pear.php-tools.net ceased to exist I made a 0.12.0 release available at pear.bovigo.org. This one is not supported, for bc purposes only and only if you still require a version running with PHP 5.2. Otherwise, it is recommended to upgrade to the 1.x series using Composer.
Tuesday, November 29. 2011
Some minutes ago vfsStream 0.11.0 was released with several bug fixes and new features. Unfortunately this introduces a BC break to the vfsStream::create() method introduced with 0.10.0. While in 0.10.0 it completely replaced the directory structure this is not the case any more. It now only adds the given structure to an existing structure, either to the directory given with the second parameter, or to the root directory without removing any other childs of the root directory. If you want to replace the whole directory tree you can use the vfsStream::setup() method which now has a third parameter. See create complex directory structures documentation for code examples.
Another new feature is the possibility to import a file system structure from disc into vfsStream. This way you can emulate an existing file structure in vfsStream without having to recreate it from scratch. See copy from file system documentation for more details.
Additionally, three bugs were fixed: unlink() does not remove non-empty directory any more, vfsStreamDirectory::hasChild() now doesn't give false positives for nested paths (patch provided by Andrew Coulton), and opening a file for reading only doesn't update the modification time any more (initial patch provided by Ludovic Chabant).
Thanks to all contributors and issue reporters for their help to improve vfsStream.
As always you can get the new release via our pear channel.
Monday, August 22. 2011
Today vfsStream 0.10.1 has been released, containing two bug fixes. The first was a problem with the vfsStream::create() functionality introduced with 0.10.0, where using numeric directories failed because of implicit type conversions (in PHP) and explicit type checks (in vfsStream). Thanks to Mathieu Kooiman for finding this issue and providing a patch.
The other fix is a mix of a documentation and type hinting fix: now you can only use directories as root instance - before it was allowed to add files as vfsStream root with with vfsStreamWrapper::setRoot(), however this failed latest when trying to access other files with a directory name. Starting with 0.10.1 you can not add files as root any more, and this was also used to change the return type documentation so you can get better auto completion when retrieving a directory object with vfsStreamWrapper::getRoot(). Thanks to Dmitry Kabanov for reporting this issue.
As always you can get the new release via our pear channel.
Friday, July 22. 2011
Not even ten days after the release of vfsStream 0.9.0 today the fresh version 0.10.0 hits the streets, containing various improvements to make your life with vfsStream much easier. Especially when you want to create more than just two or three files it's really noisy to create the directory structure. To ease this vfsStream now provides a vfsStream::create() method which accepts an array and builds the directory structure from it. See create complex directory structures for more details.
Another improvement is that vfsStream now supports flock() meaning that you can call flock() with a vfsStream file url and afterwards check the file instance whether the lock was set correctly. Another requested feature was the possibility to print out directory structures, but I had the idea of providing even more possibilities on what you could do when inspecting the directory structure. For a more detailed view on what this new feature provides check the wiki page about Visitors.
Also this release introduces a BC break. As announced with 0.9.0 the then deprecated method vfsStreamContent::setFilemtime() was removed now - please use fsStreamContent::lastModified() instead.
In order to ensure better compatibility between PHP 5.2 and 5.3 calling support for stream_set_blocking(), stream_set_timeout() and stream_set_write_buffer() on 5.3 was added. However calls with these functions always return false and do not have any influence on vfsStream, which is rather because concrete usage scenarios are a bit unclear at the moment. If you have any ideas or feature requests on how vfsStream should treat such calls feel free to add your comments on issue #15.
You can get the new release via our pear channel.
Wednesday, July 13. 2011
Today the new 0.9.0 release of vfsStream was shipped to our PEAR channel. This release ships with a new feature that allows (unit) test scenarios which make use of file access time and file attribute modification time. While there is no restriction on usage of the file access time, support for file attribute modification time is still quite limited due to the fact that PHP's stream wrapper do not support changing file attributes via chmod(), chown() and chgrp(). However, this might change with PHP 5.4 but this is some month away from today.
Another issue fixed (well, kind of) is the problem that calls to stream_select() with vfsStream-URLs did not cause problems with PHP 5.2 but with 5.3 - this release makes sure that vfsStream behaves the same under both versions. Unfortunately it continues that stream_select() does not work with vfsStream-URLs as the only possible implementation of the stream_cast() method was to return false. Actually it is expected to return the underlying resource, but there is no such resource in vfsStream, and returning resources pointing to data:// scheme URLs does not work either.
More changes are the deprecation of the vfsStreamAbstractContent::setFilemtime() method in favor of vfsStreamAbstractContent::lastModified(), which means the aforementioned is scheduled to be removed with the next minor release (most likely 0.10.0). You should change your code accordingly if you make use of this method. Last but not least there was a bug that not all given URLs were resolved correctly, leading to the problem that in the second parameter of rename() and in mkdir() no dots could be used, which is fixed now.
You can get the new release via our pear channel.
By the way, did you notice that development of vfsStream has moved to Github?
Sunday, November 7. 2010
Developers who are familiar with the topic of clean code know that static methods are a problem, especially if it comes to testability. Static makes the code global, and if there is some kind of state involved it is even more a problem because this is global state then - both a maintenance and testability nightmare.
Unfortunately, sometimes you don't get around of using such code. This could be due to a third party library you have to use, or even because of limitations of PHP itself. (Ever wanted to configure stream wrappers if stream context is not an option? Your only chance are static methods and therefore global state.) How can we keep our code clean and testable, at best without having a strongly coupled dependency to the class hosting the static method? Enter dependency injection for static methods.
Luckily PHP 5.3 comes with a new feature: dynamic access to static methods. Basically this means if you have a class with a static method you can call it like this:
Let's use this feature to get a more loose coupling between our own class and the static class. Suppose we have the following code:
For some reason the method call can not be moved to another place where we could get rid of the direct dependency. But if we change the code of the Bar class things look different:
The class still works as before, but we can change the name of the class the static method is called on. This is great, because in a test we can substitute the FooFromSomeLibrary class with our own mock implementation:
Our own Bar class is now only loosely coupled to FooFromSomeLibrary. While I don't suggest to use your own code in this way it is a helpful opportunity when working with code you don't have influence on.
Thursday, November 4. 2010
Note: this article was supposed to be released on November 3rd, but I was not able to finish and release it in time. So don't wonder if the tense in the article is not correct.
Some days ago Cem Derin proposed the idea of the Developers Shame Day. As his blog entry is German only here is a short summary of what this is about: basically, a developer should look into old source code of him/her and present a piece of code which is stupid in some kind, be it obvious errors, bad structure or security flaws - only guidelines are it most be his/her own source code and should not be changed. It must not necessarily be PHP, it could be in any language. It should be presented on November 3rd with a small description - and probably with an explanation why it is bad and makes you feel ashamed.
Initially I thought this is a great idea: showing other developers that even highly appreciated developers produce errors, sometimes even stupid ones; showing that we all started as beginners. Don't get me wrong, I still think it is a good idea. But it must be put into context. Producing bad code is nothing one should be ashamed of. Personally I believe that every developer always tries its best to produce the best code, the best solution to a problem. Bad code happens because of missing knowledge. Good thing is, you can learn how to make it better - and after applying a refactoring to the code it's not even there any more, just buried in the history of your source code control system.
However, it must be acknowledged that there is a lot of bad and even stupid code out there, even from developers who should know better. This is what developers should be ashamed of: being ignorant against what they learned from books, talks or other sources of information, not applying this knowledge to their code or to defy it because the first or even second try did not work out as expected.
So, the basic idea of the day is a very good one, showing beginners that all of us started on a low level. But I think it should not be called Shame Day - based on the context (knowledge and other constraints) in which the solution was created it probably was the best what you could do back then. And it's not about shame, because you learned and gained knowledge - which enables you to recognize something as an error or problem, and giving other developers the possibility to share your knowledge. If you take part in this day by providing a blog article about code mistakes from your past you should be proud instead: proud of having learned more, proud of sharing your knowledge with other developers and proud of defeating one of programmers worst enemy: ignorance.
Thursday, October 14. 2010
Lately we came across the problem that the XML/XSL view engine of Stubbles stopped working when upgrading to a newer PHP version which included libxml 2.7 instead of the previous 2.6 version. As of course Stubbles should be able to run with PHP 5.3 which uses libxml 2.7 this had to be fixed. So I tried to investigate the issue, and it took me some hours to find out what the problem was. Once the problem was understood it was very easy to fix. However, to come to the point were it became clear what was wrong was quite disturbing.
But, first to the problem. In the XML/XSL view engine we provide a simple to use XSL template which allows inclusion of XML parts from other XML resource files, so that you can easily share content between different routes, e.g. the header or footer of a website. The template looks like this:
<stub:include href="foo.xml" part="bar"/>
In a first step this will be transformed to something like
<xi:include href="xinc://default/foo.xml?part=bar#xpointer(/parts/part[@name='bar']/node())"><xi:fallback> ... some fallback code ... </xi:fallback></xi:include>
So, what does the href attribute contain? First, the xinc scheme points to our internal stream wrapper which is applied to transform the given XML document first before returning it for inclusion. Second, default/foo.xml gives our stream wrapper the information which xml file from which project to read. The query with the part is only additional information for debug purposes. After this, an XPointer is appended as fragment. The XPointer is used by libxml to include only the part from the returned XML document that can be accessed using the XPath expression given with the XPointer. This works well with libxml 2.6, but stopped to work with 2.7.
First I thought it might be a problem with the stream wrapper. But it was not. Neither was the stream wrapper called nor did libxml complain about an invalid URL. Next step was to try to reproduce the issue using real file URLs instead of the stream wrapper URLs. Still did not work. So, I tried to ask Google. The answers where not really helpful. Last resort: read again in the XInclude standard. After reading the final XInclude 1.0 W3C recommendation I found out that there is a separate xpointer attribute to be used. By changing the generated XInclude statement to
<xi:include href="xinc://default/foo.xml?part=bar" xpointer="xpointer(/parts/part[@name='bar']/node())"><xi:fallback> ... some fallback code ... </xi:fallback></xi:include>
it resumes working as if nothing happened.
Turns out, the XPointer part is the problem with libxml 2.7. With version 2.7 libxml completely refuses to process the XInclude if there is such an XPointer fragment appended to the URL given with the href attribute. No warning or error is thrown for this. Luckily changing the XInclude statement to comply with the final standard works with 2.6 as well.
You may ask why we used a version which was not supported by the final recommendation. Well, it just worked. And as it turns out, the first version was the way to use XPointers in XInclude 1.0 W3C Candidate Recommendation. It seems libxml just dropped support for this with the 2.7 series.
What can we learn from this:
Friday, October 8. 2010
With the release of vfsStream 0.8.0 the status is now beta instead of alpha as it was before. New features include a umask simulation which allows you to set a umask for vfsStream URLs which is then applied for new directories and files, except if you explicitly set the permissions of a file or directory using the vfsStream API. To be backward compatible and easier to use the default umask is 0000 - so if you are not interested in using this feature just don't use it and everything stays at it is.
Another new feature is the support of .. in URLs, provided via patch by Guislain Duthieuw, which means you can now have constructs like vfs://root/path/../otherpath which is mapped to vfs://root/otherpath. However, realpath() still doesn't work - there is no way to make this work with how realpath() is currently implemented in PHP itself.
Besides this two bugs have been fixed, one where it was impossible until now to access a child of a directory via the vfsStream API if the child name is contained in the parent's name. The other one was an incomplete error message when you tried to access non-existing files on the root level.
You can get the new release via our pear channel.
Tuesday, September 14. 2010
Nearly two years ago back in November 2008 I wrote about the state of annotations in the PHP world. Today I want to take the chance to revisit this and find out if annotations are still present and used, or if they are not.
Review of old candidates
Of those which I described two years ago, AttributeReader saw no further development, so I don't consider it as an option any more.
Addendum has seen new releases, the last one in October last year. The repository has seen commits recently in June this year, so I believe it's not dead, but as it is feature complete there's of course not much development activity required any more.
The third candidate was FLOW3. FLOW3 is still alive and kickin', nearing it`s final stable 1.0.0 release. Annotations are still part of the framework and the Introduction tutorial has usage examples of where you utilize annotations provided by the framework, for dependency injection and validation. The manual additionally lists persistence and even Aspect-Oriented Programming as usage scenarios. So as FLOW3 is likely to be adopted within the TYPO3 community we will see more and more FLOW3 based applications using annotations.
Another framework is Stubbles. From what was stated in the first blog post nothing has changed, beside the fact that Stubbles is now available in version 1.2.0, with versions 1.3.0 and 1.4.0 shipping within the next two or three weeks. Stubbles is used in various applications at 1&1 Internet where we make heavy usage of the annotation features.
The third framework mentioned was the XP framework. Current release is 5.7.11 with 5.8.0 right around the corner. For the annotation part nothing has changed, and XP framework is used to build the intranet of 1&1 Internet as well as a lot of internal tools.
New kids on the block
So, the three frameworks providing annotations are still alive. Question is, are there any new kids on the block? A little research reveals: yes, there are.
One of them is Pia which provides annotations and an annotation API for PHP 5.3. It seems to be under active development and there seems to be no release yet.
Nothing really usable yet, but recently a feature idea for annotation support within the Yii framework was brought up in their forums.
Another one is the Recess! PHP Framework. Recess! uses annotations for routing and ORM. I did not find anything whether Recess! allows user-defined annotations or not.
Then there is Symfony 2, currently scheduled for release in March 2011. Symfony integrates Doctrine 2 which uses annotations heavily to provide ORM features. It includes the Doctrine common annotations library which allows to define and access your own annotations. The inclusion of Doctrine 2 into Symfony 2 makes me very confident we will see a major usage boost of annotations in the PHP community.
In the first edition of this article I already speculated about the idea of having support for annotations within the language itself. Now there is an RFC for annotations including a patch which is discussed heavily on the PHP internals mailing list. Personally I would like to see this included in the PHP language, but it might be a long way ahead. It needs to fit into the language itself, and provide a syntax which integrates with the other parts of the language (though it might be noted that PHP is not very compliant in itself, but there's no need for history repeating).
However, stating that there are no uses cases for annotations in PHP is simply not true. There won't be so much ideas about annotation support if it would be useless. As more and more implementations emerge, even ones which will boost the usage within the PHP community, I'm more than confident that annotations will become widely used in PHP. Providing support in the language would be a good way to give this a solid foundation.
Monday, February 15. 2010
Seldom in a life of a developer it comes to the point where a bug can be fixed by a feature addition. This February, it happend to vfsStream. A user reported a bug where overwriting an existing vfsStream file with new but smaller content replaced only portions of the file, leaving the rest in place instead of truncating the old content before writing the fresh content. After some investigation it turned out the best fix for the problem was to implement the long scheduled feature issue 7. Thought, said, done. So today I shipped vfsStream 0.6.0 including complete support for for $mode param when opening files.
While I was at it, I added support for the $options param as well, it now evaluates whether STREAM_REPORT_ERRORS is set and acts accordingly when opening a file.
Unfortunately there are bad news as well. Due to another issue I found out that ext/zip does not work with vfsStream, and that there is no way to add support for it in vfsStream. Maybe if someone with slightly more C skills than me (which are equal to zero) might want to look into this problem for a better explanation, as I can only assume that it's due to ext/zip not supporting userland stream wrappers.
If you like the new additions get the new release.
Monday, January 25. 2010
Today I shipped vfsStream 0.5.0 which brings a new feature thanks to the efforts of Benoit Aubuchon: vfsStream now supports the rename() functionality which allows you to write test cases using vfsStream for methods that rename files. Another patch of him was to change the stat() call to respect the STREAM_URL_STAT_QUIET flag.
One more new feature is the added support for . as current directory alias so that vfs://foo/. resolves to vfs://foo - this allows to use file_exists($dir . '/.') as workaround for the failing is_executable() call on directories, as described in the comments to the is_executable() documentation in the PHP manual. Of course this raises the question if vfsStream will support .. as well - if somebody takes the time to create a patch I will incorporate this. I did not look into this issue further, but I guess it might involve recursion and a more advanced parsing of the vfs URL to make it work properly, as there might be cases like vfs://foo/bar/baz/../../dummy.
So grab the release and make use of the new features, if you like to.
Monday, January 4. 2010
About a year ago I wrote about How to get a Singleton right. While nothing has changed my opinion about the Singleton Design Pattern - contrary, I'm even more convinced that it is bad, bad and bad - I still have homework to do. In the comments to this article I was asked about my opinion on the Registry Design Pattern, and I promised it would be worth an own blog entry. Well, I did not anticipate it would take more than a year to write it, but you know, work, and no time, and yada yada yada... so here it is.
When thinking about a Registry one has to consider two points: overall architecture of the application, and implementation of the Registry itself. It's not that much what you can say about the implementation of a Registry. For PHP, it should just follow one rule to make it testable and to ease testing of classes using the registry: do not make it a Singleton. (You might have guessed that already.) PHP offers the possibility to implement the Registry as a pure static class, where data within the Registry can be stored within a static class variable, and setters as well as getters can also be static methods. There is no value in making it a Singleton, it just more stuff to type where the result is the same: global state. So if you implement it as pure static, the Registry in itself is neither bad or good.
Speaking of global state, it should be common sense by now that global state is a bad idea, at least if this global does not mean the root of the application itself, which leads us to the overall architecture of the application. How is this related to the Registry Design Pattern? The Registry is intended to allow access to configuration data, objects etc. which you don't want to pass around in your application but require them in different parts (or layers) of your applications. If you have such a need from my point of view this means the application is not fully based on the Dependency Injection principle, it does not separate object creation and business logic as much as it should. If the application is completely based on Dependency Injection, there is no need for a Registry any more.
Did I just say that there is no need for a Registry any more? Well, two exceptions. First, unfortunately in PHP there might be cases where you can not influence the creation of an object instance, and if you want to pass data or other objects to such an instance, you have to take cumbersome actions to pass those. Creation of user land stream wrapper instances is such an example, as those instances are created by PHP itself and there is no possibility to intercept this. Here a Registry might be of help, but it stays what it is: a workaround for a flaw in PHP.
The other exception is the case of using a Dependency Injection framework. You do not need a Registry here - your DI framework already has something like this. It just not called Registry, but it is it's mechanism where you bind data or objects for example in the case of Stubbles or Google Guice, and in Symfony it is called Service Container. (Please note that this is just a quick thought I had in the last days, I might be wrong on this.)
To conclude, the Registry itself is neither useful or harmful. The more important question is how strong you apply the Dependency Injection principle in your application.
Monday, September 14. 2009
This weekend I attended the PHP Unconference in the lovely city of Hamburg (if you don't count the wheather in). It was a really great event with a superb organisation, from my point of view even better then the some "professional" conferences. So, first a big thank you to the hosts from the PHP Usergroup Hamburg, you did a wonderful job.
Speaking about professional conferences, it's interesting to see that I'm not the only one with a very sceptical look on the International PHP Conference. Other attendees I spoke with had similar thoughts: always the same speakers with nearly the same topics, degrading the purpose of IPC to networking only. It would be more interesting with more fresh blood and a broader range of topics. Of course this would mean more risk for the host, but I believe in the long run the current development will ruin the IPC. Or can I just not imagine that there are so much companies out there paying several hundred Euros just for networking?
From the sessions I attended the Performance pessimization talk was really fun and insightful. Starting with an optimal (hardware) architecture Kris Köhntopp, Johann Hartmann, Stefan Priebsch and Lars Jankofsky made changes to this architecture to decrease its performance one after another. Both the audience and the speakers had very much fun in adding one "improvement" after another.
For the PHP in the Enterprise session I have rather mixed feelings. While Kris Köhntopp made some really good remarks on maturity of business models and their surrounding processes I think the whole session suffered from an undefined target. "Talking about Enterprise PHP" is not sufficient as session target. It was like a "Let's have a meeting on topic X, but we don't define an agenda for it." Well, from that point it was really enterprisy.
Together with Thorsten Rinne I did a presentation on Things to consider for testable code - I feel a bit sorry for Thorsten as his main purpose was to stress the important points of my presentation rather then talking about refactoring bad code to better code, the topic originally voted for.
To my surprise the audience seemed to appreciate it. I was very unsure if it would work out in the way I intended it. Probably I should add more examples for the Dependency Injection part as it was requested to see some real-life code, however I'm not sure how much it helped to better understand how it works. In every case I will add one or another point against Dependency Injection, as it seems that there are only benefits but no drawbacks. If you attended the session and would like to give feedback about what can be improved please drop a comment.
On sunday Oliver Müller (Btw, thanks to Oliver for taking us on a tourist tour around the Reeperbahn on saturday night. ) gave an interesting overview of current state-of-the-art technologies for implementing Single Sign-on systems. It raised new questions for a project I will do in the upcoming months.
Another interesting session was from Stefan Priebsch about the Model-View-Controler (MVC) design pattern. While I felt uncomfortable with the label "MVC" on Stubbles for quite some time now the talk finally convinced me to drop this label. MVC in the web is fundamentally different from MVC in desktop gui applications, where it was originally invented for, and there seems to be no common understanding of what MVC in web applications should really look like. That's not surprising as there are different solutions possible, and it strongly depends on the type and size of the application you create. Heck, there are even frameworks out there with a "Model" class you should inherit all your models from, which is independend of the application totally pointless and makes me cry.
Because the Deployment talk took not place due to a missing speaker I switched to a MySQL High Availability talk. I'm really glad we have database admins in our company which take care of this, but it was interesting to see what the important points are and how such an architecture is build.
The last talk was about experiences on pre-commit-hooks. It was suggested to deny commits if they do not fulfill the coding guidelines. For projects with release cycles of at least several days it seems to be really useful, but if you do several releases per day it has a high chance of getting in the way in the moment you want to deploy a bugfix for a bug you deployed earlier the day. (Please do not comment that one should not do this - it boils down to a business decision if you do it or not, wheighing less quality against time to market.) Doing a php lint check and enforcing a certain style of commit messages however seem to be useful in such projects as well.
Finally: thanks to everyone who attended. It was a great event with really interesting topics, chats and a fantastic atmosphere.
Monday, August 31. 2009
While the title of this blog entry might sound rather scary to straight OO evangelists it might attract other developers - e.g. those that played around with runkit or had a look into the Ruby world where the language supports adding new methods to a class or just to an instance of a class at runtime (see singleton methods, and this blog article; for those not familiar with Ruby: Fixnum is a class already defined by Ruby core.)
With the advent of PHP 5.3 adding new methods to an instance of a class at runtime becomes possible with PHP as well, using anonymous functions and a little bit of __call() magic. First, let's define a new class (boring old foo, bar, baz example for lack of fantasy):
As you surely already know you can add public properties to an instance at any time:
We can use this to store an anonymous function:
Unfortunately, we are not able to call this as a method:
This will blow up with a fatal error saying "Call to undefined method Foo::baz()". Of course we could do
but that is not what we wanted to achieve. Let's add some __call() magic to our Foo class:
Now we can safely call $foo->baz() with the desired result. However, compared to Ruby you can not redefine existing methods. Therefore,
will still call the method Foo::bar() defined earlier and ignore the redefinition due to the nature of how __call() works.
What can you do with it? If you use duck typing this might be useful as it reduces the amount of code required for the Adapter design pattern as you just extend the instance you want to use instead of creating a separate adapter class. The result is the same, both a full fledged adapter class and the closure can only access the public properties and methods of the instance to adapt. Problem is, the class to adapt most likely does not have the required __call() implementation.
Another use case could be a simplified version of the extension methods mechanism where you want to add a method locally without the need to have it available globally in the application.
If you have another good idea of what this can be used for please feel free to comment - just wanted to write my thoughts down.
Syndicate This Blog