Thursday, 10 November 2016

PHP & CFML: I get thrown by how static variables work

G'day:
I like it when I demonstrate to myself I'm a bit thick. Today was one of those days. I've been working with PHP for a coupla years now, and pretty much have ported my OO knowledge over to it. Coming from a CFML background one thing I'm not so familiar with is static variables and methods. I "get" it, that's cool, but clearly I'm still mid-way up the learning curve, and some idiosyncrasies still elude me. I found one today.

I was doing some testing, and the situation I was in was that my test classes needed some subclassing... don't ask... it was legit but I cannot go into details and it'd bore you rigid anyhow. In my base class I had a static variable which I needed to have a different value for in the subclass (it was actually a sub-sub-class. Dumb tests). So I set it to a new value in my test method, then the test method called a method from the base class, and the test went splat cos it was still using the base class value. This confused the shit out of me at first. Here's a simplified example:

class GP {

    public static $test = "GP";
    
    public function fromGP(){
        return self::$test;
    }
    
    public function get(){
        return self::$test;
    }

}

class P extends GP {

    public static $test = "P";
    
    public function fromP(){
        return self::$test;
    }

}

class C extends P {

    public static $test = "C";
    
    public function fromC(){
        return self::$test;
    }

}

$c = new C();

echo "fromC: " . $c->fromC() . PHP_EOL;
echo "fromP: " . $c->fromP() . PHP_EOL;
echo "fromGP: " . $c->fromGP() . PHP_EOL;
echo "get: " . $c->get() . PHP_EOL;

So:

  • I've got a grandparent class (GP) which sets a static variable, and has two methods to get its value.
  • And a Parent class (P) that extends GP, with its own value for the static variable, and another different method to get its value.
  • And a Child class (C) which does the same thing, but to the Parent.
  • In my test rig I create a C instance, and call each of the methods.
Now cos I'm thick, I expected this output:

fromC: C
fromP: C
fromGP: C
get: C

Because C overrides the value for the static test variable, so the inherited methods should all use that value.

But what I did get is this:

fromC: C
fromP: P
fromGP: GP
get: GP

It took me a while to click. I changed my variable to be not static, and got the results I expected. Changed it back: back to the "wrong" (where "wrong" is "not what I wanted") answer. Then it dawned on me I guess. Static variables are bound to the class. I already knew this, but the ramifications didn't hit me initially. $c might be an object that inherits from P and GP, and obviously object variables will also follow inheritance rules, but static variables relate to the class, not the object. So in the class GP, in which the method fromGP is defined... self::$test refers to the static $test variable in that class. So: the value is GP. and on from there for fromP and down to fromC too (and get).

It made sense once I engaged my brain.

When I got home from work I decided to "ask" Java what it thought about this, and knocked together an analogous solution. Please pardon my shit Java skills.

package me.adamcameron.test;

public class GP {

    public static String test = "GP";
    
    public String fromGP(){
        return test;
    }
    
    public String get(){
        return test;
    }

}

package me.adamcameron.test;

public class P extends GP {

    public static String test = "P";
    
    public String fromP(){
        return test;
    }

}

package me.adamcameron.test;

public class C extends P {

    public static String test = "C";
    
    public String fromC(){
        return test;
    }

}

package me.adamcameron.test;

public class Test {

    public static void main(String[] args){
        C c = new C();
        
        System.out.println("fromC: " + c.fromC());
        System.out.println("fromP: " + c.fromP());
        System.out.println("fromGP: " + c.fromGP());
        System.out.println("get: " + c.get());
        
    }

}

And that - predictably - confirmed PHP's behaviour (not that I doubted PHP. Much):

>java me.adamcameron.test.Test
fromC: C
fromP: P
fromGP: GP
get: GP

Coolio. I'm also pleased I managed to write 20-odd lines of Java and get it right firstsecond time (it was a typo! ;-)

Last but not least, I remember that Lucee's flavour of CFML introduced static variables in Lucee 5, so I decided to see what it did:

component {

    static {
        test = "GP";
    }
    
    public function fromGP(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}

component extends=GP {

    static {
        test = "P";
    }
    
    public function fromP(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}

component extends=P {

    static {
        test = "C";
    }
    
    public function fromC(){
        return static.test;
    }
    
    public function get(){
        return static.test;
    }
}


<cfscript>
o = new C();

writeOutput("fromC: #o.fromC()#<br>");
writeOutput("fromP: #o.fromP()#<br>");
writeOutput("fromGP: #o.fromGP()#<br>");
writeOutput("get: #o.get()#<br>");
</cfscript>

When I ran this...

fromC: C
fromP: C
fromGP: C
get: C


Hrm. So Lucee does what I originally wanted, but I now reckon this is not what it should be doing. I best go ask someone from LAS what the story is there.

Anyway that was that. Just a small lesson learned for me today.

Oh, btw... the solution was to just make the variable not static. Then me code worked fine. There really was no need for it to be static in the first place, on reflection.

Update:
It's important to read Kalle's comment (below) regarding static:: in PHP.

Righto.

--
Adam

Friday, 4 November 2016

Another Friday code puzzle on the Slack Channel

G'day:
Another perennial CFML community participant, Ryan Guill, has posted this week's Friday code puzzle, over on the #friday-puzzle subchannel of the #CFML Slack channel.

Don't worry about the fact it's on the #CFML channel: it's open to anyone who wants to participate (you will need to join the channel though! I'll check with Ryan if it's OK to post the direct link to the Gist for it... update... yes it is OK. Here it is: friday-puzzle-leaderboard.md (gist)).

Here's a summary of the challenge:

Challenge:

Create a function that takes an array of objects that contain a name and a date counts instances of the name for a score. Output a leaderboard of the top 10 scores. Combine the same scores together so that if, for example, first place is 20 points and two people have 20 points, show them both in first place. The order of the names in the same place are not important. Next would still be second place. For this challenge, the dates do not matter.

(but see the actual Gist for more expectations: that's just the summary).

I for one would like to see how various different languages might approach this. On the other hand, I'm pretty rammed at the moment so dunno if I will find time to actually participate, meself.

Anyway, go on... give it a blast.

Righto.

--
Adam



Thursday, 3 November 2016

PHP: different ways of defining one-off objects

G'day:
This one came from a Stack Overflow question I watched be raised, commented-on and summarily closed again because there are rules to follow, and by golly the community moderators on S/O looove following rules.

Anyway, the question got asked, but never answered.

'ere 'tis:

In PHP 7, what's the use of stdClass with the availability of anonymous classes?


As of PHP 7 we have access to anonymous classes. This means we could ditch stdClass when we need generic object as a return value... But which one would be cleaner?
  • What other differences are between those two guys?
  • Is there any performance drawback on creating an anonymous class? i.e. as each one has a different "class name", is there any additional impact?
  • Do both work "the same way" from the developer point of view, or are they actually two different beasts in some cases?
<?php
$std_obj = new stdClass(); //get_class($std_obj) == 'stdClass'
$anonymous = new class {}; //get_class($std_obj) == 'class@anonymous\0file.php0x758958a3s'

Good question. Well the bit about performance ain't, cos at this stage of one's exploration into these things - asking initial questions - one clearly doesn't have a performance issue to solve, so just don't worry about it.

OK, so I like messing around with this sort of thing, and I still need to learn a lot about PHP so I decided to refresh my memory of the various approaches here.

Firstly, the "anonymous classes" thing is a new feature in PHP 7, and allows one to model objects via a literal expression, much like how one can define a function via an expression (making a closure) rather than a statement. Here's an example:

$object = new class('Kate', 'Sheppard') {

    private $firstName;
    private $lastName;
    
    public function __construct($firstName, $lastName){
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }
    
    public function getFullName(){
        return "{$this->firstName} {$this->lastName}";
    }
};

See how go new class, and then proceed to define the class right there in the following block. Note this doesn't create a class... person1 does not contain a class; it creates an object. As evidenced by my subsequent usage of it:

echo $person1->getFullName();

Output:

Kate Sheppard

TBH, I dunno about the merits of this. In PHP one can already declare classes in the same file as other code, and more than one class in the same file. I guess it's handy for when one needs to define a one-off modelled object within another function or something? I dunno.

The other version mentioned was via stdclass, but let's not go there yet.

Now bear in mind the OP was asking about creating empty objects. One can do that with an anonymous class by just not giving the class definition any body:

$empty = new class(){};

Dumping that out gives:

object(class@anonymous)#1 (0) {
}

And with stdclass:

$empty = new stdClass();

Dumping that out gives:

object(stdClass)#1 (0) {
}

Not much to it. But I dunno if the intent in PHP is to use stdClass directly. The docs just say:

Created by typecasting to object.

It doesn't say "instantiate one of these to create an empty object". And that's where it'd say it, if that was the intent.

What it does allude to is this:

$empty = (object) [];

Dumping that out gives:

object(stdClass)#1 (0) {
}

(so same as with stdClass). To me the intended usage of stdClass is just one of inference. An array cast to an object needs to be of some type, so there's stdClass to accommodate that.

Also: how often does one want an empty object, and that's it? At least with casting an array to an object one can give it some properties off the bat:

$person = (object) [
    'firstName' => 'Kate,
    'lastName' => 'Sheppard'
];

One can add properties to the object afterwards with a new stdClass(), but that seems clunky.

Thinking about it, using new stdClass() seems about as right a way to do things as using array() to declare an array, as opposed to using a literal []. Bleah. Just use the literal.

But this all got me thinking though. Empty objects be damned. How can I implement the same as that anonymous class using these other syntaxes.

I managed to write some really crap code implementing analogues using stdclass and an object literal.

First the object literal, as it's not too awful:

$person2 = (object) [
    'firstName' => null,
    'lastName' => null,
    '__construct' => function ($firstName, $lastName) use (&$person2) {
        $person2->firstName = $firstName;
        $person2->lastName = $lastName;
    },
    'getFullName' => function () use (&$person2) {
        return "{$person2->firstName} {$person2->lastName}";
    }
];

It's not sooooo bad. Until one ones to try to use it. Check this out:

$person2->__construct('Emmeline', 'Pankhurst');
echo $person2->getFullName() . PHP_EOL;

This just gives:

PHP Fatal error:  Uncaught Error: Call to undefined method stdClass::__construct() in literalVsObjectCast.php:37
Stack trace:
#0 {main}
  thrown in literalVsObjectCast.php on line 37

Yikes. This turns out to be some weirdo order of operation things, and I can't quite remember what the story is, but it's down to how and what the function call is bound to the function, and the -> operator taking precedence over the () operator. Or something.

It's easily enough solved:

($person2->__construct)('Emmeline', 'Pankhurst');
echo ($person2->getFullName)() . PHP_EOL;

See how I've used parentheses there so the method calls are on the reference to the object/method. This works:

Emmeline Pankhurst

But it's unorthodox code, so I'll be avoiding that. Oh yeah, also notice the weirdness I had to do with the reference with the closure there. Plus having to reference the object's properties by this "external" reference. Still: good to be able to do it though.

We can go from "unorthodox" to "shit" now. There's the equiv using stdclass:

$person3 = new stdclass();
$person3->firstName = null;
$person3->initial = null;
$person3->lastName = null;
$person3->__construct = function ($firstName, $initial, $lastName) use (&$person3) {
    $person3->firstName = $firstName;
    $person3->initial = $initial;
    $person3->lastName = $lastName;
};
$person3->getFullName = function () use (&$person3) {
    return "{$person3->firstName} {$person3->initial}  {$person3->lastName}";
};

And again to run it, we need to use those extra parentheses:

($person3->__construct)('Susan', 'B', 'Anthony');
echo ($person3->getFullName)() . PHP_EOL;


Susan B Anthony

It's all a bit grim for words: the literal version was a bit awkward, but this as all the awkwardness as well as a lot of clutter (and, yes, this version also supports a middle initial, but that's not really the bulk of the bloat).

Another thing with these latter two versions is that - until the class literal version - firstName and lastName are public. I guess it's no big thing for throwaway objects, but it seems a bit "leaky" to me.

One of these JavaScript tricks we learn when trying to fit the oval peg into the round hole that is "doing OO in JS", is the Module Pattern: using an IIFE to emulate an object with private data. PHP 7 also added IIFEs, it already has closure, so we can do this in PHP too:

$person4 = (function($firstName, $lastName){
    return (object)[
        'getFullName' => function() use ($firstName, $lastName){
            return "$firstName $lastName";
        }
    ];
})('Vida', 'Goldstein');


And running it:

echo ($person4->getFullName)() . PHP_EOL;

Output:

Vida Goldstein

Now I reckon that's the tidiest syntax of all! Even beating the anonymous class version. But one is still stuck with those extra parentheses on the function call though. Pity.

In conclusion, I reckon this:

  • if you want an empty object... use (object)[]. Don't use a mechanism - anonymous classes - specifically intended to derive fully modelled objects, just to create empty objects.
  • If you want a throw-away object with a few public properties in it it: still use (object) []. It's tidier and less typing.
  • If you need some methods in there as well... go for the anonymous class literal; its usage syntax is more orthodox than using the IIFE.
  • But do remember PHP7 has IIFEs! They come in handy for things other than emulating JS design patterns.
  • I don't reckon one ought to directly use stdClass. It's clunky, leads to bloated code, and the docs kinda position it as a necessary implementation, not something that one's supposed to actively use.
  • But... generally speaking use a proper named class. These anonymous classes are not a replacement for them; they're for throw-away objects. You'll seldom need those.
  • Don't start an exercise worrying about performance. Write good clean code, using TDD, and worry about performance if it comes up. And differences like this are never gonna be where to make savings anyhow.

Thoughts? Obviously I'm just a relative n00b at PHP so I might be missing some important thing that seasoned PHPers all "get" but I've not come across yet. Lemme know.

Righto.

--
Adam

CFML: proposed new meet-up in London?

G'day:
Perennial CFML community-member Mark Drew is looking at the feasibility of getting a CFML Meetup going in London. Sounds like a good idea to me.

He's just gathering some metrics as to people's interest levels and availability at the moment... here's a survey: London CFML Meetup.

Also help him get the word out by RTing this, and in general spreading the word, eh?

If it's on days I can make it, I'll probably pop along.

Good work, Mister Drew. Am always glad to see someone trying to put back in to the CFML community.

Righto.

--
Adam

Tuesday, 1 November 2016

Ah ballocks

G'day:
I just noticed the context-highlighting thing I've got wasn't cross-referencing on that last article I just did ("PHP: what exactly are we testing?").

As it turns out it's cos my JS files were hosted on cfmldeveloper, and that service has been discontinued ("cflivedead.net").

Blogspot itself didn't / wouldn't / couldn't host JS files last time I checked: I'll check again tomorrow (I can't be arsed, this evening). If it can't host separate files, I guess I'll just in-line it. Bleah.

So anyway, sorry the cross-referencing is bung at the moment. I'll sort it out soon.

Update:

This is kinda fixed. I've inlined the JS needed on every article, but some of the article-specific JS is still not gonna work. Once I come up with a file-based solution, rather than needing to inline it, I'll sort the rest out.

Righto.

--
Adam

PHP: what exactly are we testing?

G'day:
"the rumours of this blog's demise... [etc]". Yeah. I'm still here. More about that in a separate article.

Here's a completely fictitious scenario which bears absolutely no relation to a code review discussion I had at work today. I don't even know why I mention work. This, after all, has nothing to do with my day job. Or my colleagues. I'm just making it all up.

Here's some code I just made up for the sake of discussion:

<?php 

namespace me\adamcameron\someApp\service;

use \me\adamcameron\someApp\exception;

class SomeService {

    public function getTheThingFromTheWebService($id){
        // bunch of stuff elided
        
        try {
            $result = $this->connector->getThing($id);
            
            // bunch of stuff elided
            
            return $finalResult;
        } catch (exception\NotFoundException $e) {
            // bunch of stuff elided
            
            throw new exception\ServiceException("The Thing with ID $id could not be retrieved");
         }
    }
}


Basically we're calling a web service, and the connector we use wraps up all the HTTP bumpf, and with stuff like 404 responses it throws a NotFoundException up to the service tier so that the service doesn't need to concern itself with the fact the connector is dealing with a REST web service. It could just as easily be connecting straight to a DB and the SELECT statement returned zero rows: that's still a NotFoundException situation. That's cool. We're all-good with this approach to things. For reasons that are outwith this discussion (TBH, I'm slightly contriving the situation in the code above, but maintaining the correct context) we don't want to let the NotFoundException bubble any further, we want to throw a different exception (one that other upstream code is waiting to catch), and provide a human-friendly message with said exception.

But for testing, I need to actually make sure that the Service handles this exception properly, and the two elements of this are that it chucks its own exception, and it includes that human-friendly message when it does.

So I've tested both of those.

Because I'm a well-behaved, team-playing dev, I TDD my code as I go, and having just added that exception-handling, I need to test itbeing about to add that exception-handling code, I need to write my tests first. So, anyway, whether I wrote the test before or after I wrote the code is neither here nor there. I came up with this test case:

<?php

namespace me\adamcameron\someApp\test\service;

use \namespace me\adamcameron\someApp\service\SomeService;

/** @coversDefaultClass \me\adamcameron\someApp\service\SomeService */
class SomeServiceTest extends PHPUnit_Framework_TestCase {

    public function setup() {
        $this->setMockedConnector();
        $this->testService = new SomeService($this->mockedConnector);
    }

    /**
        @covers ::getTheThingFromTheWebService
        @expectedException \me\adamcameron\someApp\exception\ServiceException
        @expectedExceptionMessage The Thing with ID 1 could not be retrieved
    */
    public function testGetTheThingsFromTheWebServiceWillThrowServiceExceptionWhenIdNotFound() {
        $testId = 1;

        $this->mockedConnector
            ->method('getThing')
            ->with($testId)
            ->will($this->throwException('\me\adamcameron\someApp\exception\NotFoundException'));
            
        $this->testService->getTheThingFromTheWebService($testId);
    }
    
    private function setMockedConnector(){
        $this->mockedConnector = $this->getMockBuilder('\me\adamcameron\connector\MyConnector')
            ->disableOriginalConstructor()
            ->setMethods(['getThing'])
            ->getMock();
    }

}

I've left a bunch of contextual and mocking code in there so you can better see what's going one. But the key parts of this test are the annotations regarding the exception.

(we're using an older version of PHPUnit, so we need to do this via annotations, not expectations, unfortunately. But anyway).

This went into code review, and I got some feedback (this is a paraphrase. Of, obviously, a completely fictitious conversation. Remember this is not really work code I'm discussing here):

  • Not too sure about the merits of testing the whole message string here. For the purposes of testing, we don't care about text, we just care about the $id being correct. Perhaps use @expectedExceptionMessageRegExp instead, with like "/.*1.*/" as the pattern.
  • not sure 1 is a great ID to mock, TBH

Well I can't fault the latter bit. If one is gonna be actually checking for values, then use values that are really unlikely to accidentally occur as side-effects of something else going on. Basically 0 and 1 are dumb test values. There's always gonna be a risk they'll end up bubbling up via an accident rather than on purpose. I usually use random prime numbers (often taken from this list of the first 1000 primes). They're just not likely to coincidentally show up somewhere else in one's results at random.

But the first bit is interesting. My first reaction was "well what's the important bit here? The number or the error message?" We wanna check the error message cos given that suggested regex pattern the exception could simply return 1, which is not help to anyone. And yet that'd pass the test. Surely the important thing is the guidance: "The Thing with ID [whatever] could not be retrieved".

But that's woolly thinking on my part. I'm not doing tests for the benefit of humans here. And no code is every gonna rely on the specifics of that message. TDD tests (and unit tests in general) are about testing logic, not "results". And they don't give a shit about the Human Interface. We've got QA for that.

There's no need to test PHP here. If we have this code:

throw new exception\ServiceException("The Thing with ID $id could not be retrieved");

We don't need to test:
  • that PHP knows what a string is
  • that PHP knows how to interpolate a variable
  • that one can pass a string to an Exception in PHP
  • etc
That's testing an external system (and showing no small amount of hubris whilst we do so!), and is outwith the remit of TDD or unit testing in general. If we can't trust PHP to know what a string is: we're in trouble.

What we do need to test is this:

public function getTheThingFromTheWebService($id){
    // bunch of stuff elided
    
    try {
        $result = $this->connector->getThing($id);
        
        // bunch of stuff elided
        
        return $finalResult;
    } catch (exception\NotFoundException $e) {
        // bunch of stuff elided
        
        throw new exception\ServiceException("The Thing with ID $id could not be retrieved"); 
    }
}


  • The ID comes in...
  • ... it's used...
  • ... and when the call that uses it fails...
  • ... we use it in the exceptional scenario.

The logic here is that it's the same ID that's passed to the function is the one reported in the exception message. Not what the exception message is: that's PHP's job.

So in reality here... we just need to test the ID is referenced in the exception. TBH, I'd not even usually bother doing that. It's just we're pushing to have more useful exception messages at the moment, so we're trying out "enforcing" this behaviour. I think it's a worthwhile exercise at least whilst the devs are getting used to it. Once it's part of our standard practice, I reckon we can stop worrying about what's in the error message.

But in the interim I'm gonna fix me test and see if I can pass this code review...

Righto.

--
Adam

Sunday, 25 September 2016

CFML: "It's alive!". Really now?

G'day:
Yesterday I was "helping" someone on the CFML Slack channel with some challenges they had with CFC serialisation. The conversation turned to ColdFusion's custom serialisers (see an earlier article: "ColdFusion 11: custom serialisers. More questions than answers"), and how pants I think the implementation & entire undertaking of that part of CF11 was. I'll just quote myself (full transcript of entire discussion here: Gist / adamcameron / slack.txt:

I did try to position the whole undertaking as "here be dragons"

On the face of it, it's a cool idea

But - as per most less-than-obvious things the Adobe CF Team tries to do - they completely cocked it up, having no real understanding of how ppl use CFML

[...]

What concerns me about Adobe's implementation is it seems to be very "not OO" which worries me when it's actually implemented by ppl who are supposedly Java devs.

[...]

I floated it "at the appropriate time" (nod to not speaking about the pre-release programme, but... well... f*** them), but the relevant parties just either didn't care or didn't get it or refused to get it.

It's the perpetual symptom that Adobe never consult the community before they blindly go and implement ill-conceived and poorly realised shite.

They ought to feel free to only consult with their major-client "IT stakeholders" for features like the API Manager and to a degree `<cfclient>` (hohoho), but when it comes to nuts 'n' bolts language implementation... they need to ask the ppl on the ground.

But based on that recent survey Adobe did... there won't be any more focus on the CFML language from them. They're only interested in buzzword-completion/box-ticking IT-Manager-pleasing platform features

In response to that someone observed they admired my sticking power with CFML (thanks for saying that), but my reaction was that Adobe finally "broke" me with CF2016 being such a waste of time for the CFML dev, as they did hardly anything in the language, instead of rolling something that's really nothing to do with ColdFusion: the API Manager into CF2016 and making it the marquee "feature".

At the same time Lucee 5 lost objective direction in that they indulged Micha's whim to re-write the thing to be OSGi capable (or whatever the correct term is), and mess around with the .lucee lang thing which ended up being both stillborn and evidence that LAS is out of their depth when they're not just copying what Adobe does. Harsh, sorry LAS, but that's my opinion.

I have given up on both the vendors when it comes to CFML, and personally I've moved from CFML to PHP now, but I'm still there in the CFML community helping people where I can. So - as far as the community goes - I'm suffering from "just when I thought I was out... they pull me back in", as it were. Apologies to Al Pacino there. But theatrics aside, that's my choice, so no worries.

Patrick came back with a reasonable riposte to my comments about being let down by the vendors:

Yeah, those sorts of public "divorces" (Railo-->Lucee) are never pretty, but it was really necessary. Kudos to the core of the Railo peeps who held it together. LAS is quite strong now, and I believe we're the future of CFML as a platform. I'm especially excited to see what the community can do with the extensible framework in place now. What say we all co-create a really vibrant ecosystem of products? With Adam as our cheerleader (I don't see it as cynical, mate!), I have no doubt we'll get it done.

Good on him, but I disagreed with one thing he said, and this lead to a bit of a diatribe from me:

You can't just say "What say we all co-create a really vibrant ecosystem of products?" and have it happen. The CFML community have demonstrated that they're - for the most part - takers rather than givers. Even with ppl asking for help from the community I see a sense of (misplaced) entitlement from them.

That said there's a minority of good worker bees, and a bunch of people who are good people but they're still just in CFML 9-5 (which is fine), but will not grow or strengthen the community.

Most of the stalwarts of the community are companies who have in the past invested a lot of effort into CFML (not the community necessarily, but for themselves), and it'd be a large $$$ cost for them to move. That's going to be why some of them continue to persist with CFML, and also encourage Lucee[:] as LAS is more likely to work with those small to mid-level companies to provide what they want out of CFML. Same as with Adobe and larger corporates.

The likes of Daemon and MSO.Net and Ortus and Pixl8 aren't going away when it comes to CFML due to how they've stacked their egg baskets, and I reckon they're more likely to stick with CFML than [it is that] LAS will [continue to] exist. Although one of them might consider "buying" Micha to perpetuate CFML for them if LAS founders. At which point Lucee will become kinda like OpenBD is for Alan Williamson's outfit

Sean was reading this and "emoting" with thumbs-ups in agreement and the like, then we got to chatting about OSS & CFML and that sort of thing. I'll spare you that.

But I stand by what I say above. I've been in the CFML community since 2001, and been active in the community all that time, and have been watching the ebb and flow of the CFML tide, be it ColdFusion or BlueDragon or Railo or Lucee. So I think my opinion is possibly an informed one. And I always offer it for the sake of informing, rather than posturing. People are quite welcome to disagree with me and to put a case forward as to why I'm mistaken. I like finding out I'm wrong, and I like debating (or arguing ;-) a topic with people. Provided they actually make a case.

Geoff Bowers (of afore-mentioned Daemon fame: he's the boss; and I think he's El Presidente of LAS) joined the conversation and suggested we should not be having such conversations, as they hurt the CFML community. And Geoff's position is that "The CFML Community" is a meaningful entity.

I disagree.

I disagree with a few points or inferences here:

  • Any conversation by experienced members of a given community will possibly have merit, even if they suggest that the best course of action for the community members is to prepare themselves for the time when "the community" doesn't really mean anything any more.
  • Even if the points being discussed have no merit, I disagree with the notion that the discussion should not be had because one member of the community doesn't like the sound of it, or it doesn't match their agenda.
  • I don't actually think "The CFML Community" is a thing in and of itself which automatically deserves preservation. This might seem odd, but I'm interested in the welfare of the members of the community, not some completely intangible label "The CFML Community". That's meaningless. I will help the individual people in the CFML community whenever I can (or, realistically, when I feel like it, but that's quite often ;-), and I think helpful advice to people who rely on CFML for their crust these days is: plan an exit strategy. It's convenient for Geoff to deny this (I'll get to that), but CFML as a language has had its day. It had a unique selling point a decade ago: it made making dynamic websites quite easy compared to where Perl or PHP or JSPs were at the time. It does not have that USP any more. CFML does a bunch of things really well, but not that much better than any number of more popular (and easy!) languages do these days. The thing is the other languages bring their own participatory communities with them, and with those communities come rich OSS efforts which ensure whatever one wants to do that might be off-piste for a language: there'll be a third-party module for that.


But I digress: no, I don't think "The CFML Community" is worth protecting. The community members are, and my best (-quality and -intentioned) advice is to start planning an exit strategy. You'd have to be an arsehole to place the preservation of "The CFML Community" ahead of the welfare of its members.

CFML has precisely one remaining relevant purpose: to keep CFML devs in work if they can't do anything else. CFML is not going places. It's staying where it is. But the ice floe where it's positioned itself is melting away, growing smaller and smaller, and nothing's gonna reverse this.

Geoff disagrees. That's cool, but other than "I disagree" and a few half-hearted ad hominems ("none taken", btw), he didn't actually refute a single thing I said. I'd be dead f***in' keen to hear what he has to say though, to demonstrate how CFML is still alive in any meaningful way though. And how it's a good prospect for uptake, and how CFMLers shouldn't be planning an exit strategy.

My cynicism suggests to me that Geoff is mostly concerned about "The CFML Community" because it's commercially prudent for him to do so: he's built a company and a livelihood on CFML. So it's beneficial to him for there to be "The CFML Community" out there, as they're potential revenue opportunities. That's completely fine and I see where he's coming from. But it seems disingenuous to me for him to be suggesting myself or others don't have the community's best interests at heart when we offer advice that is not convenient for his business model.

Another issue I'll touch on in this increasingly rambling diatribe is Geoff's assertion that my observations have an actual deleterious impact on the community. This is seemingly based on the notion that any negative comment is intrinsically deleterious, and for "names" such as myself to me making these negative comments is more deleterious still. I guess cos ppl might be inclined to listen to my advice. Heaven f***in' forbid.

He's probably right. But let's get things in perspective here. Things that are deleterious to "The CFML Community":

  1. ColdFusion being a paid-for product in 2016;
  2. Adobe's lack of interest or lack of ability to keep the language contemporary;
  3. every other bloody language being more contemporary and compelling;
  4. and free.
  5. The apathy of most CFML dev resulting in an under-represented community to engender momentum; 
  6. Adobe's lack of ability to market ColdFusion;
  7. Railo v Lucee schism;
  8. Lucee 5 taking an inordinate amount of time to see light of day, for what seems like developer-self-indulgence;
  9. LAS dithering about the place and seeming very incoherent when it comes to what they're doing;
  10. [daylight]
  11. [more frickin' daylight]
  12. Me saying stuff Geoff doesn't like.
One could quibble about some of those items there, but it's the scale of things that is significant. Almost all of CFML users don't know who the f*** I am. A decent subset of those that do can think for themselves and will take what I say with a grain of salt, or already be (dis-)agreeing with me. And the others could do worse than consider what I have to say, and weigh it up for themselves.

But no CFMLer who cannot form their own opinions will have any idea who I am, and will never see any of my written opinions.

I hasten to add that the list above is "things that are deleterious to the CFML community". I see Lucee as a net force for good in the community. But if we're assessing what is unhelpful in the community, then they're a more significant player in all this than I am. And even Lucee's influence one way or the other is completely inconsequential compared to Adobe's.

But go on then, Geoff and others who think CFML is alive and well and is something that should be suggested for new adopters. Tell me why it is. Tell us... "The CFML Community"... why that's the case. Don't just call me an uppity dick or a big meany or whatever the f*** personal attack is easier for you to make. Do something.

Why is CFML still relevant?
Why should new people adopt it?
Why should people stick with it instead of perhaps protecting their future by examining other options?
Why should people not at least suggest that might be something to do?

Go on then...

--
Adam