I need to refactor an ancient piece of Perl code with defined(@array) and defined(%hash)

363 Views Asked by At

I'm running an ancient version of Movable Type that works just fine for me. However, I began to receive the following server errors:

defined(@array) is deprecated at /home/public_html/cgi-bin/mt/extlib/Locale/Maketext.pm line 623.
defined(%hash) is deprecated at /home/public_html/cgi-bin/mt/extlib/Locale/Maketext.pm line 623.

I found the line in question:

if defined(%{$module . "::Lexicon"}) or defined(@{$module . "::ISA"});

Is the following the correct way to refactor this line?

if %{$module . "::Lexicon"} or @{$module . "::ISA"};

If so, why? If not, why not? I'd like to understand better what happened to defined(@array) and defined(%hash).

UPDATE: I also found a similar issue on line 367 of CGI.pm:

if (defined(@QUERY_PARAM) && !defined($initializer)) {

I rewrote this as follows but I'm still not sure if it's right:

if (@QUERY_PARAM && $initializer) {

I can see how @QUERY_PARAM could confirm that it exists, but I'm probably not setting the second condition that $initializer does not exist, and I'm not quite sure how to do this.

2

There are 2 best solutions below

2
mob On BEST ANSWER
$ perl -w -Mdiagnostics -e 'print defined(@array)'

Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at -e
        line 1 (#1)
    (F) defined() is not useful on arrays because it
    checks for an undefined scalar value.  If you want to see if the
    array is empty, just use if (@array) { # not empty } for example.

Uncaught exception from user code:
        Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at -e line 1.

$ [perldoc -f defined](http://metacpan.org/pod/perlfunc#defined)

...

Use of "defined" on aggregates (hashes and arrays) is no longer
supported. It used to report whether memory for that aggregate
had ever been allocated. You should instead use a simple test
for size:

     if (@an_array) { print "has array elements\n" }
     if (%a_hash)   { print "has hash members\n"   }

The defined(@array) construction never did what users expected it to do, and what it actually did was not that useful, so it was removed from the language (and deprecated in the version of Perl you are using). The way to fix it, as the diagnostics and docs suggest, is to just use @array rather than defined(@array) in boolean context.

0
lordadmira On

As mob said, the solution is just to do if (@array)... or if (%hash)....

However, for anyone too curious, you can inspect the symbol table to see if that variable has ever been autovivified. More however, however, there is (almost) no reason to ever do this. All it would tell you is if you are the first line of code to use that variable. Which is 5 nines of useless.

But for that 6th significant digit, here's how you do it. The only use case I even know of is in Carp where it checks for some variables in the calling namespace without polluting it in the process.

This only works for global, non-lexical, variables. Global is undeclared, use vars qw//, and our(). Lexical is my() and state(). The symbol table is the special hash that contains all of the global symbol names in your package.

use Data::Dump qw/pp/;

pp \ %Z::;
{}

$Z::foo = "foo scalar";

pp \ %Z::;
do {
  my $a = { foo => *Z::foo };
  $a->{foo} = \"foo scalar";
  $a;
}

print "symbol exists\n" if exists $Z::{foo};
symbol exists

print "symbol and array exists\n" if $Z::{foo} and defined *{$Z::{foo}}{ARRAY};

pp \ %Z::;
do {
  my $a = { foo => *Z::foo };
  $a->{foo} = \"foo scalar";
  $a;
}

print "symbol and array does not exist\n" unless $Z::{foo} and defined *{$Z::{foo}}{ARRAY};
symbol and array does not exist

pp \ %Z::;
do {
  my $a = { foo => *Z::foo };
  $a->{foo} = \"foo scalar";
  $a;
}