Helper library for modules written in PHP?

Has this idea been explored?

I have started writing a set of PHP classes to be located here https://github.com/Appdynamics/ansible-php. The idea is to, as part of the task, if the module is determined to be written in PHP (check shebang), then copy this library over to the right place. The PHP process would be called with a custom INI file that would have include_path set properly. The module then only needs a short amount of code to start with just like a Python-based Ansible module. The primary use case would be with websites written in PHP that need easier interfacing than using Python. Typical site types include Drupal, WordPress, and sites written with the Syfmony 2 framework. While all of these have command line helpers (drush and wp-cli, Symfony 2 console), it is quite useless to have ‘changed: true’ every time you run a command (via command or shell modules) when nothing has really changed.

Example module (the only boilerplate is the first 3 lines):

#!/usr/bin/env php

<?php require 'Ansible.php'; // class auto-loading file or just includes all of them $module = new AnsibleModule( array('dir' => array('type' => 'directory'), 'set_mode' => array('type' => 'bool'), 'mode' => array('choices' => array('production', 'staging', 'dev')) ); $include = $module->params['dir'] . DIRECTORY_SEPARATOR . 'somecode.inc'; $should_set_mode = $module->params['set_mode']; // === true or false, because of the type key in the argument specification $target_mode = $module->params['mode']; $changed = false; // Here, require the PHP code at the $dir argument, and set the application to the mode if $should_set_mode === true // If an error occurs, call $module->failJson(); optionally with an array of data $module->exitJson(array('changed' => $changed)); One area of concern is compatibility, so I am avoiding using new features such as namespaces. Perhaps the oldest version to go down to is PHP 5.2 (influenced mostly by CodeIgniter's standards). If this were to be seriously explored and merged into Ansible core, I would like to know where these files would go. The lib/ directory seems the most likely place.

Hi Andrew,

We’re unlikely to merge any PHP stubs into Ansible core because we cannot easily maintain those things as a Python shop/community, nor do we want to have any PHP modules in core.

However, I believe in supporting mix-in stubs from various languages and making common code insertion pluggable.

Right now this is leveraged by:

https://github.com/ansible/ansible/tree/devel/lib/ansible/module_utils

And this covers Python based mixins.

I’d say I’m somewhat interseted in making this a little more extensible, but I’m also not positive I’d want to maintain that overhead.

My gut feeling is you should find ways to write these things in Python, perhaps the Python modules could be simple stubs that called a CLI based in PHP if they needed to talk to PHP libraries.

You can of course write modules in non-Python today, but there just isn’t a mechanism for sharing things without having pre-installed deps.

But maybe sharing by deps isn’t so bad either.

I think it’s not - if I am misunderstanding and this is about subtle tweaks around enabling things like WANTS_JSON to work a little better with PHP in the code that sends that over, I’m open to that, but I would almost think that would look like the ruby example repo in github.com/ansible/ - but correct me if I’m wrong.

I have finished the project for now. Just so in case anyone is interested https://github.com/appdynamics/ansible-php . It requires PHP 5.3+ and Composer and all that. The README details everything.

Yes, it involves copying over the dependency but that actually is not as bad as I thought. If you already have a PHP project working with Composer you can just add this to it and use the composer module in Ansible to update dependencies. Unfortunately as said in the README you must provide a ansible_php option with every usage of a module written with this ‘framework’. And also, the boilerplate mentioned in the README is a bit ugly but necessary.

I think the best approach here is to get into your own code as soon as possible from your module, and then report changes. This may require changes in how your code functions (to get changes for example) but it’s for the best IMO because it means better information during and after deployments.

Andrew

Seems like a whole lot of trouble to avoid learning python. I don't see why this is any better or really necessary otherwise.

Where did I write about avoiding learning Python? It is obvious you have not read any of this thread nor the project page.

I've absolutely read over this thread and the project page. You've ported the ansible module helpers to PHP so you can write your modules in PHP rather than learn some Python and use what's already there.

I have written several modules in Python. I am very familiar with Python. That is my go-to language for just about anything. All of the problems I am solving with this library I have already done workarounds for either in the tasks (combined with other tasks usually) or in Python-based modules, but they are very ugly and not close to as good at reporting information back.

If some code (an Ansible module) needs to check something that is not readily available as a file, and non-trivial to get to in Python, I think the best way to get that information is by hooking in directly to that API compared to coming from the outside with a Python-based module, executing a script via command/shell (like Drush or WP-CLI as explained in my documentation, which sometimes give no indicators to state change; and also can be very slow with no chance at optimisation), or worse yet attempting at re-implementing any of that code in Python (which is pointless because all of these sites call upon 3rd party code that has to be called properly in order for the database to stay consistent). In terms of Drupal and WordPress, these are not well-designed frameworks in that sense. No one will disagree with that. But they still exist and people still use them. I simply want to provide reliable deployment with as much accurate information as possible, and to minimise the copy-paste of code in any PHP-based module.

In addition to providing an API similar to Ansible’s own but in PHP, I am also trying to alleviate some non-obvious PHP pitfalls like error/exception handling (all errors call a fail_json()-like function and provide (as useful as possible) a backtrace) and JSON encoding (which properly calls json_last_error() in PHP every time). Having a ‘simple’ (one file) PHP module would be nice, but then you would have copy-paste in every module you create in PHP. And that would be the same case in Python if Ansible did not provide a library.

This would not be any different to me if a large code base was written in any other language, and has no Python API (or REST API) to check/change things (which is the case for a lot of things). I would still considering putting together a module in that language (or whatever works best) so I can get the best access to the information and give as best as possible feedback on changes made during deployment.

What I do not want is that just because it is some non-Python thing and/or cannot be reliably be managed via Python that the command/shell module says ‘“changed”: true’ every single time. That is generally useless especially if the command is actually idempotent in one way or another but Ansible (even with changed_when and inspecting stdout or rc) cannot detect that.

Finally, nobody is forced to use this for PHP or anything else. It is merely a suggestion. But it does hook into existing projects relatively easily, with only a few caveats. And indeed there are people out there who will not approach writing an Ansible module because they do not know Python. I do not see anything wrong with providing a tool.

One way for many things to return changed status appropriately would be use of “creates=” and “removes=” for things that create files.

However, I do understand if you are writing a module that needs to make 5000 PHP API calls and no CLI already exists for Ansible to cleanly shell out to.

If you limit this to things that only interact with PHP APIs, I think what you are doing is fine, so what this really is is a small set of utility functions to return things in JSON, etc, that made it a little easier for you to write modules in PHP.

That’s fine, in most cases though, a python module that calls a CLI would be a better route. I don’t know all of your cases though, since I’m not a PHP guy and don’t know those mentioned tools.

Since this is just making sure you return some valid JSON, and this isn’t fundamentally different from how Ruby modules work today.

Example repo to show how cross-language modules work:

https://github.com/ansible/ansible-for-rubyists/
https://github.com/ansible/ansible-for-rubyists/blob/master/library/my_ruby_calculator

What I would caution is that if you are working with a team of people that do know Python, and also want to contribute things back to Ansible, this is crossing a boundary where we can’t take those things back in core. This is the main problem I would see, and you can possibly make things harder to work on in your team if people are used to Python -but Ansible isn’t a purist in this category by any means. Just that all core modules are Python, and more utility functions are always going to be available there, because that’s where we spend time.

An example of using a CLI vs an API here is our Ruby gem library is still useful, but it’s written in Python, because it uses the “gem” CLI tool and doesn’t need to be written in Ruby. The other advantage of this is that if the API were to change, the CLI usually stays the same, so it’s more loosely coupled.

I will at least agree with Tim in that PHP is a terrible language, but in the end it doesn’t matter if that’s what you have to talk to and a job needs to be done :slight_smile: