Nephtali 3.3.2 Released: Streamlined Pure Pipe Registration and Many Enhancements/Fixes

Finally, Nephtali 3.3.2 is released. The biggest addition to this release are the new registration functions that greatly simplify working with pure pipes. That said, there are several other improvements, and I’ve made a quick list of them below:

  • Removed required_ports key from registor_action (vestige from days of old.)
  • Deprecated ‘is_valid’ flag in favor of ‘is_set’ flag for pure pipe signature checks.
  • Corrected omission of ‘error_message’ spelling check for ports in debug mode.
  • Fixed JSON-formatting bug for REST-ful port validation requests containing multiple errors.
  • Port ‘error_message’ option now properly overrides the default error messages generated for specific failures.
  • Started tradition of adding version number to the ncore root docblock to facilitate identification of the current version of Nephtali you’re using.
  • Fixed bug leading to unnecessary calls of curl parallel processing function.

As usual, let me know if you see any issues.

Next, either integrated unit testing (now that the syntactically pure pipe functions are done, unit testing should be quite enjoyable) -OR- using caching capabilities to parse HTML files, create AST’s and store optimized code for databinding pages (should lead to big performance payoff.) What would you like next?

Posted in New Feature, New Release, Performance, Unit Testing | Leave a comment

New registration functions for pure pipes

I suspect many of those who viewed the recent enhancements to Nephtali that promote pure pipe functions thought the example code was terribly verbose. Frankly, it is, for the most typical situations. With that admission, let me say that there is a method to my madness.

I firmly believe developers should strive to account for as much complexity as possible in the early stages of development.  After establishing a strong, extensible foundation from which to work, we can then examine various use cases of developers to identify the most common needs, and work to provide tools (general functions, API enhancements, hooks, plugins, etc.) that facilitate these high-traffic scenarios.

General pipes

In terms of Nephtali pipes, I first created a general pipe registration function. While using the general pipe can sometimes require significant amounts of code, it is extensible to every situation I’ve encountered while using Nephtali.

n\pipe\register(
    $name = 'general_pipe',
    $function = function($markup, $args, $signatures)
    {
        // make it happen 
        return n\view\render($view = 'default', $markup);
    }
);

Action pipes

After working with Nephtali for a while, I realized that I could greatly facilitate one particular use case: performing specific actions if/when sets of ports are present on a request. This lead to the development of action pipes.

n\pipe\register_action(
    $name = 'user',
    $actions = array(
        n\port\signature(n\val('update_user')) => function($markup)
        {
            // typically have port validation code here and db update, but omitted for brevity
            return n\view\render($view = 'update', $markup);
        },
        n\port\signature(n\val('insert_user')) => function($markup)
        {
            // typically have port validation code here and db insert, but omitted for brevity
            return n\view\render($view = 'insert', $markup);
        }
    )
);

General pure pipes

Most recently, Nephtali had added the ability to work with functions that, at least in terms of syntactic appearance, are pure. The benefit to this type of approach is that unit testing a function that appears syntactically pure can be done with relative ease, as you can push in any set of values to a pipe’s function at test time to ensure that the function’s logic is correct.

n\pipe\register(
    $name = 'user',
    $function = function($markup, $args, $signatures)
    {
        if ($signatures['update']['is_valid']) {
            if ($signatures['update']['errors']) {
                return n\view\render($view = 'feedback', $markup, $signatures['update']['errors']);
            } else {
                if ($args['update']) {
                    return n\view\render($view = 'default', $markup, $rows = array('message' => 'User updated.'));
                } else {
                    return n\view\render($view = 'error', $markup);
                }
            }
        } else
        if ($signatures['insert']['is_valid']) {
            if ($signatures['insert']['errors']) {
                return n\view\render($view = 'feedback', $markup, $signatures['insert']['errors']);
            } else {
                if ($args['insert']) {
                    return n\view\render($view = 'default', $markup, $rows = array('message' => 'User inserted.'));
                } else {
                    return n\view\render($view = 'error', $markup);
                }
            }
        } else {
            return '';
        }
    },
    $opts = array(
        'signatures' => array(
            'update' => array('id','name'),
            'insert' => array('name')
	),
        $args = array(
            'update' => function($signatures) {
                return n\sql\action\update($table_name = 'users', $inputs = $signatures['update']['values']);
            },
            'insert' => function($signatures) {
                return n\sql\action\insert($table_name = 'users', $inputs = $signatures['insert']['values']);
            }
        )
    )
);

I think we can agree that the above example is painfully verbose, especially when one compares this code to equivalent code in other web frameworks. However, the above code is also very capable, and extends to situations where one may wish to manually work with many different sets of ports and sub-pipes, whilst keeping the ability to maintain the purity of the function (again, the purity I’m speaking of is the syntactic purity, which affords easy testing of the functions logic and correctness, as you can pass in any array of values to the $args array at test time.)

Now, having worked with the above code for several weeks, I’ve developed two functions that handle the most typical use cases.

Pure display pipes

One of the most common use cases is wanting to display a feedback view if one or more of the ports that the view requires is/are invalid, display a default view if there is data available, display an empty view if there is no data, or display an error view if there is an error during the processing of the pipe. This can now be accomplished through use of the n\pipe\register_pure_display() function.

The pure display pipe expects a default, empty, error, and feedback view present in the markup for the pipe. And, just as with the other pipe functions, you can also pass in an optional $opts argument to set the databinding (form or placeholder); whitelists; override the default, feedback, or empty functions; etc.

n\pipe\register_pure_display(
    $name = 'users',
    $rows_func = function($port_vals){
        return n\sql\source\query($query = 'SELECT * FROM users');
    }
);

Pure action pipes

As noted above, wanting to performing specific actions if/when sets of ports are present on a request is a common use case for Nephtali users, and the n\pipe\register_pure_action() function  facilitates this workflow utilizing the new pure capabilities. The first action to have a valid signature (all of the ports are present, but not necessarily valid) will be executed.

The pure action pipe expects the markup to contain a feedback view for displaying error messages if any of the ports are invalid, a status view for displaying the status message (databound to its {message} placeholder within the <!–data–> region), and an error view.

n\pipe\register_pure_action(
    $name = 'user',
    $actions = array(
        'update' => array(
            'io_func' => function($port_vals){
                return n\sql\action\update($table_name = 'users', $inputs = $port_vals);
            },
            'signature' => array('id', 'name'),
            'message' => 'User updated.'
        )
        'insert' => array(
            'io_func' => function($port_vals){
                return n\sql\action\insert($table_name = 'users', $inputs = $port_vals);
            },
            'signature' => array('name'),
            'message' => 'User inserted.'
        )
    )
);

Conclusion

Although not officially released yet, you can pull the changes from trunk to start utilizing the new pure functions to speed your development now. I won’t be changing the API at this point, although I may add a couple other features before making the next release. Hopefully you’ll find the new functions as useful as I do, and with this work out of the way, the next step will be to integrate unit testing directly within Nephtali.

Posted in Frameworks, Functional Programming, New Feature, New Release, Software Development Practices, Unit Testing | Leave a comment

Update immediately from Nephtali 3.3.0 to 3.3.1 for a bug fix

Sorry, there is a big string escaping bug in Nephtali 3.3.0. Please update immediately to Nephtali 3.3.1. The unit test didn’t show the issue because I only added one data row and because of caching, one row didn’t show the issue, you need 2 rows to see the bug.

Again, my apologies.

Update Feb. 23, 2012: To clear up confusion, the bug did not leave sites vulnerable in terms of security. Rather, Nephtali’s default behavior to escape all output overrode any whitelists that were set up to pass through the escaping mechanism. What this means is that sites were not left vulnerable to XSS attacks, but they were likely vulnerable to ugly aesthetics when expected tags were escaped.

Posted in New Release, Security | Leave a comment

Nephtali 3.3.0 is live

Nephtali 3.3.0 is now available for download. You can view what’s new in Nephtali 3.3.0 in the previous blog post entitled “What’s coming to Nephtali 3.3?

There are many new enhancements (including some performance improvements) and some big new features, so hopefully you take the time to read up. That said, the biggest new features are changes to the API that promote the usage of pure functions within pipes, making it easier to write correct code and integrate unit testing.

And, as always, please let me know if you find any issues.

Posted in Functional Programming, New Release, Performance | Leave a comment

What’s coming to Nephtali 3.3?

What’s coming to Nephtali 3.3? Lots of stuff!

Basic updates

First, let’s get to the basic updates.

  1. Pipes can be set to be optionally displayed if the markup for the pipe is present (just set is_optional to true in $opts array for n\pipe\register().)
  2. Ports can now be registered in bulk through n\port\register_bulk().
  3. Regexes updated for url and US phone.
  4. Many micro-optimizations implemented.
  5. Many improvements to doc-blocks within the API.

Now, before we move on to discuss the big new features, let me give you some background.

Functional programming languages light my fire

I love functional programming languages. Nephtali’s design principles clearly trace their roots to my experience with functional languages such as LISP, Haskell, Erlang, and others. While PHP is obviously not a functional programming language, through the Nephtali framework I’ve sought to embrace general functional programming principles such as immutability, laziness through use of a registration model, and using arrays for everything under the sun.

One of my favorite aspects of functional languages is the value placed in keeping functions “pure.” You can read what Wikipedia says about pure functions if you’re not familiar with them, but in short, a pure function will produce the same output for a given set of arguments every time the function is called because no other state is brought into or altered by the function. Pure functions are easier to reason about in terms of correctness, easier to test, and probably even increase life expectancy :)

Some of you who are coming from functional languages are now saying to yourselves, “Yes, these principles of functional programming languages are great, but what could this possibly have to do with the syntactically challenged language PHP?”

I built a framework for PHP because of practical reasons. I develop websites for a living and my clients don’t care what language I’m using, they just want an effective, reasonably priced, maintainable website. PHP is a very practical language choice in light of these considerations. And, through Nephtali, I’ve been able to simulate some of the functional programming features I’ve missed the most. Now, back to the discussion of pure functions in Nephtali.

Nephtali 3.3 will promote pure pipe functions

While I can’t force particular functions to be pure in PHP, I can try and promote pure functions through the features and conventions in Nephtali, and Nephali 3.3 provides you with some handy features that facilitate keeping a lexically-pure pipe function. Let’s work through an example contrasting the code of an old-style Nephtali action pipe (I must stress Nephtali 3.3 is fully backwards compatible with this style) with the newer, pure-promoting style.

Old-style action pipe

n\port\register(
    $name = 'id',
    $opts = array(
        'max_length' => 10
    )
);
n\port\register(
    $name = 'company_name',
    $opts = array(
        'max_length' => 50,
        'filter' => n\constant\FILTER_TEXT
    )
);
n\port\register(
    $name = 'description',
    $opts = array(
        'max_length' => 500,
        'filter' => n\constant\FILTER_TEXT_MULTILINE
    )
);
n\port\register(
    $name = 'url',
    $opts = array(
        'max_length' => 200,
        'filter' => n\constant\FILTER_URL
    )
);
n\port\register(
    $name = 'did',
    $opts = array(
        'max_length' => 10,
    )
);
n\val($name = 'update_host', $value = array('id','company_name','description','url'));
n\val($name = 'insert_host', $value = array('company_name','description','url'));
n\val($name = 'delete_host', $value = array('did'));
n\pipe\register_action(
    $name = 'host',
    $actions = array(
        n\port\signature(n\val('update_host')) => function($markup)
        {
            if ($rows = n\port\validate(n\val('update_host')))
            {
                return n\view\render($view = 'feedback', $markup, $rows);
            }
 
            n\sql\action\update($table_name = 'hosts', $inputs = n\port\get(n\val('update_host')));
            return n\view\render($view = 'default', $markup, $rows = array(array('message' => "We've updated the host")));
         },
        n\port\signature(n\val('insert_host')) => function($markup)
        {
            if ($rows = n\port\validate(n\val('insert_host')))
            {
                return n\view\render($view = 'feedback', $markup, $rows);
            }
 
            n\sql\action\insert($table_name = 'hosts', $inputs = n\port\get(n\val('insert_host')));
            return n\view\render($view = 'default', $markup, $rows = array(array('message' => "We've added the host")));
         },
        n\port\signature(n\val('delete_host')) => function($markup)
        {
            if ($rows = n\port\validate(n\val('delete_host')))
            {
                return n\view\render($view = 'feedback', $markup, $rows);
            }
 
            n\sql\action\delete($table_name = 'hosts', $id = n\port\get('did'));
            return n\view\render($view = 'default', $markup, $rows = array(array('message' => "We've deleted the host")));
         }
    )
);

I won’t talk much about this example as most Nephtali users are familiar with this approach. However, let me just point out the amount of work it takes to either mentally work through correctness of the code OR write unit tests for it.

New pure-promoting pipe

n\port\register_bulk(
	$ports = array(
		'id' => array(
			'max_length' => 10
		),
		'company_name' => array(
			'max_length' => 50,
			'filter' => n\constant\FILTER_TEXT
		),
		'description' => array(
			'max_length' => 500,
			'filter' => n\constant\FILTER_TEXT_MULTILINE
		),
		'url' => array(
			'max_length' => 200,
			'filter' => n\constant\FILTER_URL
		),
		'did' => array(
			'max_length' => 10,
		)
	)
);
n\pipe\register(
    $name = 'host',
    $function = function($markup, $args, $signatures)
	{
		if ($signatures['update']['is_valid']) {
			if ($signatures['update']['errors']) {
				return n\view\render($view = 'feedback', $markup, $signatures['update']['errors']);
			} else {
				if ($args['update']) {
					return n\view\render($view = 'default', $markup, $rows = array('message' => "We've updated the host"));
				} else {
					return n\view\render($view = 'error', $markup);
				}
			}
		} else
		if ($signatures['insert']['is_valid']) {
			if ($signatures['insert']['errors']) {
				return n\view\render($view = 'feedback', $markup, $signatures['insert']['errors']);
			} else {
				if ($args['insert']) {
					return n\view\render($view = 'default', $markup, $rows = array('message' => "We've inserted the host"));
				} else {
					return n\view\render($view = 'error', $markup);
				}
			}
		} else
		if ($signatures['delete']['is_valid']) {
			if ($signatures['delete']['errors']) {
				return n\view\render($view = 'feedback', $markup, $signatures['delete']['errors']);
			} else {
				if ($args['delete']) {
					return n\view\render($view = 'default', $markup, $rows = array('message' => "We've deleted the host"));
				} else {
					return n\view\render($view = 'error', $markup);
				}
			}
		} else {
			return '';
		}
    },
	$opts = array(
		'signatures' => array(
			'update' => array('id','company_name','description','url'),
			'insert' => array('company_name','description','url'),
			'delete' => array('did')
		)
	),
	$args = array(
		'update' => function($signatures) {
			try {
				return n\sql\action\update($table_name = 'hosts', $inputs = $signatures['update']['values']);
			} catch (Exception $e) {
				return false;
			}
		},
		'insert' => function($signatures) {
			try {
				return n\sql\action\insert($table_name = 'hosts', $inputs = $signatures['insert']['values']);
			} catch (Exception $e) {
				return false;
			}
		},
		'delete' => function($signatures) {
			try {
				return n\sql\action\delete($table_name = 'hosts', $id = $signatures['delete']['values']['did']);
			} catch (Exception $e) {
				return false;
			}
		}
	)
);

In the new style, the pipe function is structured so that you can quite easily reason about the flow AND write unit tests for it. No contrived mock objects, no IO gotchas, just pass in the arguments representing the states you want to test and voila, you’ve got a test (mind you, if you haven’t carefully reasoned the correctness of your code, your test will suck, but I know you like seeing pretty green lights, so…)

The “how” behind the above code owes much to the magic of the $args argument. While it looks like an array, it’s actually an object that implements the appropriate interfaces so it can be accessed like an array, keeping consistent with Nephtali’s M.O. in that regard. It stores the functions and only processes a function once you try to access the value. However, any further access to the variable makes use of a cached value result, so you don’t have to worry about calling an insert function more than once by accessing the value later on in your code, for example.

The beauty in the $args argument is that it encourages you to keep as much logic as possible in the easily tested pipe function. Only pull out the basic state-accessing/changing operations into the $args functions, and you’ll have code that’s easier to reason about and to test.

Coming soon, to a server near you…

P.S. – You can obviously refactor the new pure-promoting style so it is much shorter, as I’ve done below (and, indeed, it could even be shorter, but this gives you just a quick idea), but I didn’t include this in the body of the article out of fear that it’s harder to conceptually work through what’s going on in terms of the new features.

$function = function($markup, $args, $signatures)
	{
		$valid_sig_processor = function($signature, $arg, $message) {
			if ($signature['errors']) {
				return n\view\render($view = 'feedback', $markup, $signature['errors']);
			} else {
				if ($arg) {
					return n\view\render($view = 'default', $markup, $rows = array('message' => $message));
				} else {
					return n\view\render($view = 'error', $markup);
				}
			}
		};
 
		if ($signatures['update']['is_valid']) {
			return $valid_sig_processor($signatures['update'], $args['update'], "We've updated the host.");
		} else
		if ($signatures['insert']['is_valid']) {
			return $valid_sig_processor($signatures['insert'], $args['insert'], "We've inserted the host.");
		} else
		if ($signatures['delete']['is_valid']) {
			return $valid_sig_processor($signatures['delete'], $args['delete'], "We've deleted the host.");
		} else {
			return '';
		}
    }
Posted in Functional Programming, New Feature, New Release | Leave a comment