Flourish PHP Unframework
This is an archived copy of the forum for reference purposes

Sutra 1.2-beta release

posted by audvare 7 years ago

I have been working on a sort of unframework for a little while (although at first it was intended to be a complete framework with routing and similar functionality not in Flourish). Instead, I have opted to leave it more open just like Flourish does and so the classes I have made (all of which begin with 's') are designed to be used just about anywhere, be it your MVC framework, or generic procedural code, or even CLI scripts.

A short description of what each class intends to do or help with:

sArray - array utility functions

sAuthorization - has a method requireNotLoggedIn() that does the reverse of requireLoggedIn()

sCache - extension of fCache but makes each key unique so cache keys (like those of APC) do not collide

sGrammar - extension of fGrammar; has a method dashize() that does the same as underscorize() but with dashes; it uses fURL::makeFriendly() but also supports camelCase notation

sHTTPRequest - a simple object class to do HTTP requests from the server side

sImage - a questionable extension to fImage; this was mainly made because fImage::rotate() does not take into account IFD* exif data that contains the orientation information; has a flip() method for flipping pixels vertically or horizontally or both (this is required by some orientations)

sJSONP - like fJSON, but for sending JSONP responses

sProcess - An object class to run processes, including interactive processes; tested on Windows as well

sRequest - Generally, when I handle a POST request, I redirect the user after. This means that if the user hits the back button, first they are not bothered by that annoying message that is meaningless to 99% of users, but also it means that the data from the form is not put back by the browser (which means less usability). sRequest::savePostValues('some_id') can save the POST values into session for later retrieval. You can delete the values when the form is submitted successfully by calling sRequest::deletePostValues(). You can restore the POST values (calls fRequest::set()) when re-rendering the form after a validation error by calling sRequest::setPostValues(). This class also has a non-throwing method to check a CSRF token string, useful with fValidation::addCallbackRule().

sResponse - For general HTTP header responses. More to come in this class.

sTemplate - This is an alternative to fTemplating which is similar to PHPTemplate. If you have used Drupal in the past, consider sTemplate::buffer() to be equivalent to Drupal's theme() function and the .tpl.php files will not be unusual to you. Features CSS minification optionally using CssMin and CDN support. See the documentation and tutorial for more details.

sTimestamp - This is an extension fTimestamp that can receive a RFC3339 timestamp as well as whatever can be parsed by fTimestamp.

https://github.com/tatsh/sutra

Of course, this unframework is dependent on a potentially patched Flourish (see the patches directory).

There is a tutorial here on how to get started fast: https://github.com/tatsh/sutra/wiki/Tutorial

Documentation: http://tatsh.github.com/sutra/

I'm doing a final check before the final version of 1.2 is released. Will be very soon.

Ideas for version 1.3: https://github.com/tatsh/sutra/wiki/Roadmap

And of course if you would like to join, simply fork, and make a pull request with your changes. My standards are almost the same as Flourish', but just a few changes: 'public static' instead of 'static public', bracket on the same line for (public static function {), end bracket on separate line (not '} else {'), and 2-spaces instead of real tabs.

My only suggestion would have been to do this as a fork to Flourish and either create patches to be applied, or wait for the full move to github.

I plan to be adding a fJSON::encodeWithPadding() method for JSONP to my fork tonight -- timing is rather coincidential.

A few of the newer classes seem interesting, but what were your reasons for not forking and keeping the same class names/structure?

posted by mattsah 7 years ago

I am actually waiting for Flourish to officially be on Github before I do any serious forking because then Will will be able to accept pull requests and it will be a lot easier to collaborate. I really do not even want my sImage class because I want Flourish to have the 2 methods it has properly done with the operations queue that fImage uses. (1.3 is likely to not have the sImage class as I hope Flourish will have the methods and ideas merged in, unless fImage fully becomes easily extendable.)

Maybe someday there will be no Sutra project and it will simply be a fork of Flourish. But as for now, it made more sense since there is no easy collaboration, to just extend where possible.

Otherwise, the structure is relatively the same I thought? I'm just using the letter s instead of f. Like Flourish, I have a classes directory and a tests directory (although the tests directory is not the same because I do not have the same requirements as Flourish for testing). I think this naming style is a neat alternative to PHP namespaces (whether using _ to emulate or actual namespaces with the namespace and using keywords). I have actually got into arguments about this with those writing standards (like PSR-0) as they are very pro Vendor
Namespace
Style
class names and completely against global namespaces. It's a new road of PHP I do not feel like going down at all, especially in my personal projects (this being one of them). This makes PHP far too complicated and even more Java-like (I prefer the term corporate-style), and I am strictly not a fan. They are also encouraging the use of auto-loading when it has been made quite clear again and again that APC and others are better off with code loading with static includes (which is what fLoader does, and my sLoader class).

I did forget to mention the class sHTML which is just a general class for making HTML element strings with valid attribute values, but its use is not exactly fully encouraged for everything since it can be a performance bottleneck from my experience so far.

posted by audvare 7 years ago

I agree to some extent with the namespacing. In particular, I feel like frameworks *should* use the global namespacing. The reason I think Flourish can also get away with global namespacing is because of the breadth of the library. It's very unlikely that I will need other general purpose classes outside of Flourish.

Namespacing makes sense for more specific libraries, namely because it's a good way to ensure a certain level of modularity. If 10 people release a TwitterOAuth class for PHP, having the vendor specified helps for someone who might release a general twitter app that requires that library and wouldn't work if you got the wrong one.

This all being said, my general point was just more about forking, which you answered. Even though I can't send will push requests, I still find it's much cleaner to fork the mirrored github repo so I can merge from upstream cleanly when he does updates. Also it doesn't require all the patches stuff you're doing.

For me, part of the balance overall is what should be in the general purpose library vs. what should be in more of a traditional framework. I don't think a general purpose library should say much at all about loading, static or auto, as such, I stay away from fLoader and the like.

Also on sHTTPRequest and sResponse. It might do good to heavily focus on the internals of HTTP with some of these. sResponse, for example, seems a bit limited with regards to how extensible it would be to add other HTTP error responses. One of the things that I've taken great care to do with inKWell's Controller class is focus heavily on the full expanse of HTTP. I realize you're classes might be a bit younger, but it's something I certainly recommend baking in early, as it took me some time to refactor my previous mistakes.

That said, sHTTPRequest::getJSON for example should send more specific Accept headers to define which types it will accept. The web seems to be moving pretty heavily away from extensions on files, which means the accept headers become much more useful for requesting specific formats of data.

90% of my personal projects now do not differentiate the URL for JSON vs. HTML, even by extension, so my code ends up doing something like this:

switch(self::acceptTypes('text/html', 'application/json')) {
    case 'text/html':
         return $view;
    case 'application/json':
         return fJSON::encode($view);
}

Although the framework also supports adding a request_format parameter for much more ambiguous accept headers (which most normal browsers send), I am seeing the reliance on this as somewhat legacy. In short, you should be able to say "I want JSON and only JSON" and the server should respond with either JSON or saying "Sorry we don't support JSON." Although the actual content it responds with to say they don't support it can, by the RFC, at that point vary... the HTTP status code *should* not.

Just some thoughts. You can read much more specifically how this works here: http://inkwell.dotink.org/docs/http_support -- you will also note that it handles mismatches between the Accept headers and the request format. So let's say I set up a route like the following:

'/users/:username.:request_format'

And someone then requests /users/mattsah.json but Accept headers only specify that they want text/html -- this will actually throw a 404, since the URL is trying to indicate the request format, but it's unavailable. Much more common, however is for me simply to implement /users/:username and depend quite strictly on the Accept header they send.

posted by mattsah 7 years ago

Right now I'm just keeping the library very simple. I use Moor for routing typically and it is there I handle responses. The sResponse class is just for organising the various headers that are not so easy to remember (I might even rename it to sHTTPHeader or something like that). I know a true 'response' class would handle everything HTTP can do. Generally however, I am coding for myself and my exact needs. I welcome others to join in if they choose to use my code.

I was originally intending for Sutra to not be a generic set of code. It was going to include well thought out routing, database handling, etc. But then I decided I did not like that. It was too limited or hacky. If you look in the commit history, you can see a large commit (merge) that deleted most of Sutra 1.0 (which was tagged before that commit).

Rather, I am giving the programmer the option on the way to route, how to manage databases (you really don't have to use fDatabase if you don't want to), and responses. sCore is merely a suggestion and is how I am doing sites. I extend from sCore and write those methods as well anything else that the specific site might need in general (methods like isProductionMode() and similar things).

I will consider adding the Accept header to send in the getJSON method in sHTTPRequest.

I rarely look at the incoming Accept headers in my code but I may start to do so. My general rule with AJAX is JSON only. I really do not see a reason to send other formats for AJAX requests.

if (fRequest::isAjax() || (!myCore::isProductionMode() && fRequest::get('ajax', 'bool', FALSE))) {
  fJSON::sendHeader();
  $data = array();
  // handle getting data...
  print fJSON::encode($data);
  return;
}

// HTML version if necessary

On a side note, one site I have implements OAuth 1.0 (using the pecl-oauth extension) and only sends back JSON data. Rather than read Accept headers, this fact is documented.

posted by audvare 7 years ago

By the way a little more on namespacing and its current incaration in PHP. Symfony2 and Zend are of course very into namespacing and I wouldn't disagree with those projects since they are very large (however I question their need as Flourish does nearly everything (except routing) Symfony does without using real namespaces). It's not really to do with the separator (although I still have criticisms about that). It is more to do with claiming that code is getting that complicated. I do like that PHP goes back to mix and match however this is not like how we use Flourish in combination with large libraries such as HTML Purifier. This is mix and match as long as you are using a framework like Symfony2 or Zend.

I disagree things are this complicated or getting to that point. Maybe you understand my viewpoint since I know you stress the KISS principle in your inKWell project. It looks like I'm on the outskirts of some kind of 'corporate revolution' of PHP where everything is getting the features of Java (in reality, emulation). I am happy to be at this position. None of my code is so complicated I need namespaces to solve collision issues or to provide 'organisation'. Truly if I ran into an issue like the one you described, I would fork and rename the class, for I do not wish to begin using namespaces in a project that is not using them.

And more on the Flourish style of namespacing as opposed to underscore/real namespacing (with many useless subdirectories); I do not understand the point of this:

  VendorName/
    NamespaceName/
      Exceptions/
        BadValueException.php

where of course, this would map to a namespace named:
VendorName
NamespaceName
Exceptions and a class BadValueException (prior to real namespaces: VendorName_NamespaceName_Exceptions_BadValueException <- of course this is horrible and why I got away from anything Zend back then (like Magento)). But someone needs to explain to me why the subdirectories are even necessary. This is not organisation by any means. Having to navigate several subdirectories to get to the file in question means there is a problem.

Furthermore, on all the projects I have done with Flourish so far, it satisfies all my needs except for what I write as library code of course, be it in Sutra or in the project's private ('proprietary') code. One website I worked on has classes that begin with p and they may extend from Flourish/Sutra and another I am working on has h as the prefix. hYouTube for example is a static class that deals with the YouTube API (it even uses fXML to parse responses).

This is an easier to use directory structure (say the site was called Hello :) ):

site-root/
  flourish/ prefix: 'a'
  moor/     prefix: 'Moor'
  sutra/    prefix: 's'
  hello/    prefix: 'h'
  hello/hLoader.php <- extends sLoader (which extends fLoader), calls parent:: methods
  model/ <-- fORM classes; the 'M' in MVC; of course nobody is forcing the use of fORM
  model/User.php
    (there is no prefix because it should be very obvious what this class is,
      especially since it extends fActiveRecord;
      should be seen as such in your IDE)
  router/ <-- router classes/files; the 'C' in MVC; no prefix required
    when using Moor, I use the suffix 'ActionController' because my routers extend off MoorActionController
  template/ <-- suppose this could be where templates exist for use with fTemplating; the 'V' in MVC
  index.php

index.php:

require 'hello/sLoader.php'; // loads all classes, including model and router
hLoader::best();

(also if you look at the Sutra tutorial you can see that I do not actually put anything in the site root except the bare minimum; all the general utility code is in /usr/share/php via ebuilds).

I'm happy to stick with my personal projects staying far far away from real namespaces in PHP, even with all the fanfare. There is no need for this level of complication. And also, having to type '
strlen' to get the global strlen() in a namespaced section of code is stupid. I actually tried using Flourish with namespaces and got so fed up with typing '
fRequest; and similar that I finally figured out to be like Flourish and adopt a single letter as a prefix for my general utility classes (such as those in Sutra).

posted by audvare 7 years ago
posted by audvare 7 years ago