Search code examples
perlmod-perl

problems comparing variables assigned within a foreach statement


I have been working on a cgi file that will check if a username is already taken when a user wants to register their credentials. If the username is taken it is supposed to notify them, if not it saves their credentials to the original flat file. I am having trouble comparing variables that I have assigned value to within a foreach statement. I am telling the foreach to assign a username to a variable if the name that a user input is the same as one that is already stored. I have it assigning the variables properly, but after words I want to tell it to compare those variables again outside of the foreach so the operation is only being done once. Here is my current code

#!/usr/bin/perl 
use warnings;
use strict;
use CGI qw(:standard);
use CGI::Carp qw/fatalsToBrowser warningsToBrowser/;
use Digest::MD5 qw(md5 md5_hex md5_base64);

#telling what variables are still to be used as global
our ($username, ,$user, $nametaken);

#assigning some local variables
my $username = param("username");
my $password = param("password");
my $hashpass = md5_hex($password);

print header, start_html();

#creating an array from the flatfile that usernames and passwords are stored
my @users = do { open my $fh, "<", "password.txt" or die $!; map { chomp; split /:/ } <$fh> };

#comparing the values in the array to the username entered
foreach my $user (@users) {
if ($user eq $username) {
    #printing here to test if it is comparing correctly which it is
    print p("$user\n");
    #assigning the $user value to $nametaken so it can be compared to later
    my $nametaken = $user;
    #printing here to test if the variable was correctly assigned, which it is
    print p("$nametaken\n");
    }
}   

#printing here to test if the variable was correctly assigned, which it is not printing
#so the foreach must be causing some king of issue for this variable after it is done and I don't know what that is
print p("$nametaken\n");

#Here is where I am trying to check if the username already exists and then save the user credentials if it does not
if ($nametaken eq $username) {
print p("Username already taken, Try again");
}

#As of now the else statement is running everytime and saving new user credentials even if a username is already taken
else {
open  my $fh, ">>", "password.txt" or die $!;
print $fh "$username:$hashpass\n";
print p("Your account has been created sucessfully");
close $fh;

}
print end_html();

Solution

  • You are declaring a NEW lexically scoped variable $nametaken inside of your foreach loop - or rather, inside the if {} block: my $nametaken = $user;

    It may share the same name with $nametaken variable you had outside, but it's a completely different variable, with the scope of that if block - once you exit the if, that variable is completely forgotten. Whatever value you assigned to it is lost.

    You can see more details about lexically scoped variables here:

    http://perldoc.perl.org/perlsub.html#Private-Variables-via-my%28%29


    To solve your problem tactically, you simply need to remove the my declaration from within the if: $nametaken=$user;

    To do it correctly, in a Perl way, you should re-think your approach to the problem alltogether. You CAN use a foreach loop to detect if a value is in the list, but it's definitely NOT the best (readability wise, or sometimes even performance wise) Perl technique. A more idiomatic way is to use hash lookups:

    my %users = map { ($_ => 1) } @users; # Create a hash with users being keys
    if ($users{$username}) {
        print "$username already taken!\n";
    }