The ternary conditional operator without the second argument is a bit novel, but it is similar1 to the null-coalescing operator found in other Algol-derivative languages like C# and Perl, and as you mention, the ||
operator in Ruby (and JavaScript).
It looks weird at first, but it's not too out there (especially since there's precedent for similar operators in other languages), and can save a good deal of keystrokes. And, if what happened with the namespace delimiter (\
) is any indication, weird syntaxes will eventually be adopted by the PHP community.
But one of the major problems PHP applications tend to face is the (sometimes) excruciatingly long lag time between release of a new version of PHP and when hosts start to support it. This leads to issues where you need to be backwards compatible with older versions of PHP, forgoing the use of convenience changes like this.
If that's not a concern to you, and your team agrees on its usage (or if you're a solo developer, if you're comfortable with it), by all means go for it. Like the namespace delimiter, I really think it's really going to be a matter of when, not if, it'll be acceptable in all future PHP projects.
Note 1: But not identical, given null-coalescing operators only test on non-null values (and not truthy values like PHP), and the ?:
syntax doesn't suppress undefined notices as you mentioned in the comments.
I could be waaaaaay off, but...
It seems that a model is necessary to use the library, you just don't care which one, right? Further, the library doesn't care how the model gets its data, or where the data comes from, it just wants an object that meets the criteria of the interface.
If the library doesn't need the connection object, then make that the responsibility of the model.
interface MyFooModelInterface
{
public function getItem($id);
public function addItem($item);
...
}
And here is a simple class example:
class MyLib
{
protected $model;
public function __construct(MyFooModelInterface $model)
{
$this->model = $model;
}
public function getItemById($id)
{
return $this->model->getItem($id);
}
}
And a Model example:
class MyModel implements MyFooModelInterface
{
protected $conn;
public function __construct(\PDO $conn)
{
$this->conn = $conn;
}
public function getItem($id)
{
...
}
public function addItem($item)
{
...
}
}
And here is an example of using it:
// the model is responsible for handling the data retrieval, not the library
$model = new Model($conn);
$lib = new MyLib($model);
return $lib->getItem($id);
Now, if I decide that I want a file-based solution, I can easily swap the model. To test the model, the connection obj is injected, so that's easy. To test the library, the model is also injected, so, again, that's easy.
In this case, the model is only responsible for getting the data, while the library is responsible for handling and processing the data. Neither cares what or how the other one does it.
EDIT
the library with a factory method to create the model if none was given...
class MyLib
{
protected $model;
public function __construct(MyFooModelInterface $model = null, $conn = null)
{
// if we have a model, use it
if (!is_null($model)) {
$this->model = $model;
}
// if we don't have a model, but have a connection obj, make a model
if (is_null($model) && !is_null($conn)) {
$this->model = $this->factoryModel($conn);
}
// if we don't have either, quit and go home
if (is_null($model) && is_null($conn)) {
throw new Exception('need moar inputs');
}
}
protected factoryModel($conn)
{
$this->model = new MyModel($conn);
}
public function getItemById($id)
{
return $this->model->getItem($id);
}
}
Best Answer
If you can't do method chaining, don't do method chaining.
That's probably the least error prone way to do this. If the null-case is to be expected in the normal course of operation, you should not use the exception system to handle this.
PHP 7 adds a null coalescing operator
??
, but not a null-propagating method call operator (e.g.?.
in C#). With such an operator, a call$a?->b()
might be equivalent to$a === null ? $a->b() : null
.One idiom you might sometimes see is assigning the variable inside the condition:
This style makes more sense in other languages where this would also restrict the scope of the assigned variable to the conditional, and when there is no error handling – if the return value is null, there's nothing to do.