Returning an Array from a Function

I have been taught that a Function should always return a value, but there are lots of times when I need it to also return a label.

Here is am example of what I mean…

In a User’s Message Center, on the left side are three folders: Inbox, Sent, Trash.

Next to the “Inbox” folder, I would like a number in parentheses denoting how many New Messages there are. (e.g. “Inbox (3)”)

Here I just need a value returned from my Function showing $newMessages.

However, in the Page Header, I also want to tell the User the how many New Messages there are, because when the User isn’t in the Message Center, they might still like to see this info.

In this case, I want things to be a label like this… “3 New Messages”.

And it is not as easy as just returning $newMessages and wrapping it in a label like this…


echo "<span>$newMessages New Message<span>";

Why?

Because, I want things to be grammatically correct, so it should be…


"0 New Messages"

"1 New Message"

"2 New Messages"

I don’t want to muck up my calling script with all of this logic!!

So, would it be okay to calculate $newMessages inside my Function, and then return an Array which contains both a VALUE (e.g. “3”) and a LABEL (e.g. “3 New Messages”)??

Doing this makes my Function much more flexible, and prevents me from having to create TWO Function to do similar, yet different things.

What do you think?

Sincerely,

Debbie

Returning an array is perfectly fine. Array manipulation functions return arrays all the time.

One function -> one return doesn’t mean the return has to be one literal value. It just means it needs to be one entity that can be stored in a variable. It can be a string, an array, an object, etc., without breaking the doctrine.

I get that, but what about my larger question…

Is it bad to return a “label” (e.g. “3 New Messages”) or even output (e.g. “<span>3 New Messages</span>”) from a Function?

Also, is it bad to allow a Function to calculate a “value” (e.g. “1”) and then offer to return the “value” or alternatively a “label with value” (e.g. “1 New Message”)??

Some people would say a Function should only ever return a literal value.

Some people would say that Markup and Formatting should be done outside of a Function.

And some people would say a Function can return different data-types, but that you should pick one type (e.g. “values” or “formatted label”) and stick with that one representation.

What do you say about those things?

Sincerely,

Debbie

Hi Debbie,

I would say that it’s better to separate out the functionality which deals with formatting/output, as that is display logic and should go in your view layer.

When faced with questions like this, ask yourself: What would happen if I needed to make my program display output in multiple languages? What if I wanted to add a different interface to my program (e.g. an API)? In both of these cases, having display logic coupled to your model is going to make things more difficult. Taking the first example, if you added another language, you’d have to alter your function to return an additional label. If you return labels from multiple functions around your app, then that’s potentially a lot of work.

In an application I was working on recently I needed to do the same thing, with a singular or plural label depending on the value given. I passed the count value to my view model and used a helper function to pluralize the word depending on the value.

Hi Debbie.

Further to what @fretburner mentioned about separation of functionality I have found that it is best only to pass data back and to format the contents in your View.

It may seem like a good idea at the time to CSS format data in your Model and/or Llibrary functions but next week when trying to debug and pinpoint why certain data is formatted is difficult.

An alternative method to extract user posts/data instead of using an array is to use “Pass Variables by Reference”.
This is achieved by prefixing function parameters with an ampersand;

View:


 # Defaults
  $newMsg = 0;
  $inbox      = 0;
  $sent        = 0;
  $trash       = 0; 
  echo '<dl>';
      $spc = ', ';
      for( $i2=1; $i2<6; $i2++):
        getUserEmail( $i2, $name, $newMsg, $inbox, $sent, $trash); 
        echo '<dt>'  .$name .' (' .$i2  .')</dt>'; 
        echo '<dd>Messages:  '.$newMsg .$spc .$inbox  .$spc .$sent .$spc .$trash .'</dd>';
      endfor;
  echo '</dl>';



# Model and or Library Data 
function getUserEmail( $id=0, & $name='DebbieDee', & $newMsg, & $inbox, & $sent, & $trash)
{
  $result = FALSE;

  $name     = getName($id);
  $newMsg = getNew($id);
  $inbox      = getInbox($id);
  $sent        = getSent($id);
  $trash       = getTrash($id);

   $result     =  $newMsg + $inbox + $sent + $trash; 

  return  $result; # 0 means NO Change 
}# getUserEmail(...)

function getName($id) 
{
  $name = array('Not Used', 'DebbieDee', 'Tom' , 'Dick', 'Harry' , 'Mary Poppins'); 

  return $name[ $id ];
}#

function getNew($id)
{
  return rand(0,5);
}#

function getInbox($id)
{
  return rand(0,5);
}#

function getSent($id)
{
  return rand(0,5);
}#

function getTrash($id)
{
  return rand(0,5);
}#

Output:

DebbieDee (1)
Messages: 1, 3, 1, 2
Tom (2)
Messages: 3, 5, 3, 2
Dick (3)
Messages: 2, 2, 5, 4
Harry (4)
Messages: 2, 5, 0, 1
Mary Poppins (5)
Messages: 2, 3, 4, 2

If you decide to return array in your methods, make sure to use PHP’s arrayobject or splfixedarray instead of PHP native array. PHP’s native array is not object oriented and using them bring more troubles than benefits.

I’ve always used plain old native arrays in my code… could you perhaps elaborate on why you think it’s preferable not to use them?

I think I already explained it clearly in my last comment. PHP arrays are not object oriented, this leads to problems like ‘calling member function on non-object error’ for me every now and then and it’s not that easy to debug. In a perfect script everything is an object, the way PHP native arrays were designed was flawed to begin with, it may be a bit late to fix them now but fortunately PHP does offer Arrayobject and SplFixedArray to compensate for such troubles.

Not really. You said that native arrays were not objects (a fact), and that they bring more trouble than benefits (your opinion) but you didn’t explain why and I think that’s important when you’re telling someone that they should or shouldn’t do something.

So what your saying is, you forget that you’re dealing with a native array rather than an object, and that makes them bad?

Unfortunately, primitive types aren’t objects in PHP either, so the ‘perfect’ script is going to remain out of reach.

NO.

Passing variables by reference leads to sloppy spaghetti code in no time that is damn near impossible to maintain. A function should not do anything to the program state other than make a return. Ever.

In my opinion PHP shouldn’t even have pass by reference. It doesn’t need it. It’s based on a memory management trick in C. PHP doesn’t let you address memory management concerns, yet time and again I see pass by reference used in an attempt to accomplish this. Which doesn’t really sync with how PHP internally works - PHP doesn’t actually copy values passed to a function until you change a value. The pass by reference operator simply suppresses this copy behavior.

Stay away from Pass by Reference. It usually causes more code testing headaches than problems it solves. I’m not going to say it is totally without use, but its uses are limited.

@Fretburner:

Oh my you really try to take every word out of me, dont ya. The fact that PHP array is not object oriented bring more troubles than this ‘calling member functions on non-object’ error, I admit its not the best example I can give. One problem I frequently notice from third party scripts is that they are passing array by reference using the ampersand operator &, which suggests that PHP array aint pass by reference by default. The fact that you have to remember to use ampersand to pass PHP array by reference is another source of error that is really difficult to debug.

On the other hand, its a design flaw that PHP’s array is a combination of numeric-indexed array and associative array(linked-hashmap). This implementation severely violates single responsibility principle, the usage of an array can be ambiguous unless heavily documented. I’ve had trouble using some PHP built-in functions as they return an array that I have to read the documentation thoroughly to figure out whether the array is numeric or associative.

When I say PHP array is not object oriented, I mean more than just the syntax. Good object oriented design principle requires this separation of concerns and makes sure that each object/type exhibits one particular behavior, PHP array offers nothing like that. In Python and Ruby, they offer different classes to handle these two type of arrays. There’s also a reason why I brought up both ArrayObject and SplFixedArray, use the former for associative array/HashMap and the latter for numerically index array is a good practice. Of course you can actually use SPL’s data structure like SplLinkedList if you want to, its for advanced programmers who have strong background with data structures.

At last but not least, using PHP native arrays can promote bad programming habits of building multi-dimensional associative arrays that really should’ve been presented as object composition. One example I’ve identified is from the book ‘Zend Framework 2.0 by Example’, here’s an example of the code it uses(can you identify the dimension of this array?):


return array(
          'controllers' => array('invokables' => array('Users\\Controller\\Index' => ''Users\\Controller\\IndexController',
                                                              )
          'router' => array(
              'route' => array(
                  'users' => array)
                      'type' => 'literal',
                      'options' => array(
                          'route' => '/users',
                          'defaults' => array(
                              '_NAMESPACE_' => 'Users\\Controller',
                              'controller' => 'Index',
                              'action' => 'index',
                           ),
                       ),
                       'may_terminate' => true,
                       'child_routes' => array(
                           'default' => array(
                               'type' => 'Segment',
                               'options' => array(
                                   'route' => '/[:controller[/:action]]/',
                                   'constraints' => array(
                                       'controller' => '[a-zA-Z] [a-zA-z0-9_-]*',
                                       'action' => '[a-zA-Z] [a-zA-z0-9_-]*',
                                   ),
                                   'defaults' => array(), 
                               ),
                           ),
                       ),
                       'view_manager' => array('template_path_stack' => array('users' => __DIR__ . '/../view')),
           ),
        ),
    ),
);

Okay did you bother reading this entire code through? Do you consider the multi-dimensional array close to readable and maintainable? To me an array with more than 2 dimensions is a poor programming/design practice, and it should be strictly forbidden for an array to go for more than 3 dimension. I am not sure if this is how Zend Framework is supposed to work, but at least the way the author of the book wrote such a program demonstrates that PHP arrays are dangerous and can be easily abused. If you do have to use PHP native arrays, use it sparingly.

Thanks for the additional responses, but way out of my league…

I’m not using OOP.

I’m not using MVC.

Last night I broke thinks up differently. I created a Function that returns $totalMessages so I can use it in two places.

Then I created a Function which calculates the $pctUsed and which returned a formatted value.

Maybe in v3.0 I can get into OOP and MVC and then a lot of this stuff will take care of itself?!

Sincerely,

Debbie

You don’t work with large datasets in normalized databases very often do you? It only takes a single join to get to a 3 dimensional return once the data is collated. And it is definitely possible for more with nested joins.

Consider a customer table, an orders table, and a products table. If you want to pull an order history for a group of customers then you will have to start from the customer table, join the orders, then join the products to the orders. If you then collate the data for display before passing it out of the model into the view you will have an array that is, at a minimum 4 layers deep. It will be even deeper if the product table must also be joined to something.

HoF, you seem to be a very knowledgable guy and if I’m ever in the same city as you I’d happily buy you a beer and sit around and chat sometime, but you got to back off the “Only lousy programmers” do X pronouncements. More than once you’ve shown that you while you have a lot of theoretical, classroom knowledge on programming, your real world experience is lacking. I know this because I was there once myself making the same stupid statements. Do a search on this forum of my posts and you might even find a few.

Keep an open mind on things. Be aware that in programming everything is done for a reason. Even in the worst code out there gets coded the way it is for a reason. Once you find out why its done that way sometimes it makes sense. Sometimes your first reaction is confirmed.

As I said earlier in this thread - pass by reference is usually bad. There are outlying corner cases. I’ve ran into a few, and I’m sure there are others out there.

Arrays have their place in the language. Even heavily nested ones do. While you can create a specialized object for every data scenario you’d use an array for, what do you gain for all that time? A lot of crap code exists not because the author was a dunce, but because he or she was rushed.

In my world the only thing that really matters is delivering the project on time and on (or under) budget. So I will continue to use plain old arrays, because all the reasons you cited cannot justify the cost in time to avoid their use.

As far as returning html markup, I think that’s a matter of choice. Making a function that can be used in different situations is perfectly fine in MHO. Say you call a list of links and a display option. You could return table rows, bullet points or just links with line breaks. If the result is best formatted as an array then that’s how I will format the result.

How about something really simple.

$newMessagesLabel = $newMessages !=1 ? "s":"";
echo sprintf("You have %s new Message%s",$newMessages,$newMessagesLabel );

Keeping code that deals with the display of data separate from the code that deals with retrieving that data is basic separation of concerns. It’s good practice, regardless of whether your code is procedural or OO, or whether you’re using MVC or not. It makes your code easier to maintain and reuse.

@Michael Morris:
Yes I never worked with large database system that requires this many inner-joins before, you have a point. Id say that there’s no absolutely right or wrong on a per se basis, you can always find exceptions however unlikely they are. For this, I agree with you.

However, I do see a major difference from the multi-dimensional array code I found from that Zend framework book and the example you posted. In your scenario, there is an intrinsic constraint/barrier with the database system that the dataset must be returned as arrays and multi-dimensional arrays. In this case, you have no other way to get around other than using multi-dimensional arrays. Similarly if a PHP legacy function requires a multi-dimensional array as its argument, and that there is no other alternative for you to choose from, you are stuck with it and you have to construct a multi-dimensional array to get around with this limitation. The fact that multi-dimensional arrays are present and useful in some cases does not contradict with the fact that in general it’s a bad programming practice, most of the times it’s there because there’s no better alternatives. In the sample code I posted, on the other hand, there are way many better alternatives than constructing a router with a 5-6 dimensional associative array, and they are more elegant, modular and maintainable. Using multi-dimensional array is probably the worst way of constructing a router object, if it has to be done this way, then either the author or Zend Framework 2’s design was flawed.

Also the argument of development time/cost is quite controversial as well. Using a specialized object for some data scenarios will help for a project that is expected to grow in time with new features being added and old features being maintained. If you work on freelancer projects in which each task is once-and-done, you probably do not need to worry about that. Nonetheless, for many projects future insights of design are important. Using arrays may be easier and faster to launch the initial product, but extending the application and fixing bugs will be harder and more time-consuming(sometimes even impossible without big refactoring). It’s about development vs maintenance costs, its about present and future costs. it’s a tradeoff you will need to make, while my personal experience tells me that long-term planning tends to win out in most circumstances, at least for what I’ve worked on. Sure I’ve been in situations that the project/task is rushed, that happens all the time in college era when you are required to write a matlab program assignment that is due the following week. At times like that you’d hardly worry about quality, but its not the same story when we are talking about enterprise application design/development.

Sure our perceptions differ when our experiences differ, but I do believe that there are some certain programming practices/rules that must or at least should be enforced except in extreme circumstances. I did not say that you should never use PHP native arrays, there are times you have to live with it, especially with PHP’s built-in functions that accepts or returns arrays, there’s no way around that. Nonetheless, I do believe that PHP native arrays should be used sparingly, it’s not supposed to present a complex structure that is better suited in object composition representation. Even with the multidimensional array database system you brought up, you only use the multidimensional array for retrieval of information from DBMS. After obtaining the multi-dimensional dataset, you will create domain objects to store and manipulate the information. In this way, you are indeed using multidimensional arrays sparingly, you use it when you have to use it.

Sometimes a script is badly designed since the coder is rushing his/her time, but in many occasions it is because they are not good programmers. Take the application I am currently working on as an example, I took it over from an original owner who initially wrote the program in procedural spaghetti code. I wondered why the script was designed this way, turned out that this was all he was capable of. I am pretty sure many posters on this forum are just like that, which is why I’m trying to bring up OOP and good programming practices. Sure advanced coders like you dont need to listen or learn, but I believe it will help beginner coders. If they are working on projects with an intense schedule, they are free to just ignore the advices, since all they care about is an exact answer to a particular problem. But if they are ever interested in improving their skills in a long run, these will come in handy. I assume most newbie posters come here to learn, not just to wish for an answer to their upcoming urgent programming tasks. Feel free correcting me if this assumption is wrong.

I apologize if I sound intense, I am just trying to prove a point and I respect your knowledge and experience.

Hall of Famer

Previously to using PHP I used C++ and liked the “Pass By Reference” feature. Some people reckon that it is more efficient and preserves memory.

I appreciate your comments and to reduce problems I always add a “ByRef” suffix to passed variables. The code I supplied should have been:



  for( $i2=1; $i2<6; $i2++):

      getUserEmailbyRef ( $i2, $nameByRef, $newMsgByRef, $inboxByRef, $sentByRef, $trashByRef); 

      echo '<dt>'  .$nameByRef .' (' .$i2  .')</dt>'; 
      echo '<dd>Messages:  '.$newMsgByRef .$spc .$inboxByRef  .$spc .$sentByRef .$spc .$trashByRef .'</dd>';
  endfor;


A simple MVC approach is to treat Model Functions as a “Black-Box” Third Party Library. Both requests and returned results are made according to an agreed specification.