I inherited a two-year-old PHP application and a problem was just identified. Being a generalist who is still learning PHP, my present level of expertise isn't enough to help me.

The application calls another script to authenticate against an MSSQL database, and then queries that database for educational course information. The user picks a state from first dropdown, which controls which list of schools is shown in the second dropdown. When both state and school are picked, the browser URL bar is populated with a query. So far, so good, except that any school that happens to have an ampersand in the name of the school will break the query because %26 is not being used in the place of the ampersand. They are taken to the results page fine, but the returned data halts right at the ampersand.

For example, the user picks "IA", then "Iowa State Univ Science & Tech", and gets this broken query:

http://www.example.com/phpapplication.php?state=IA&city=&school=Iowa State Univ Science & Tech&button_trans=Submit+Query

While on the broken results page it's possible to edit the URL and rerun the query correctly. Corrected query:

http://www.example.com/phpapplication.php?state=IA&city=&school=Iowa State Univ Science %26 Tech&button_trans=Submit+Query

That doesn't help the user of course, but at least it reassures me that this will probably require a pretty simple fix.

I've been experimenting, but so far I have not managed to locate a good fix or workaround. Urlencode and rawurlencode both seem to break this even further, and I've tried out str_replace as well but can't seem to get the syntax right. I would appreciate help from anyone who might have keen enough eyes to locate the problem or lend insight. I'll post the full code next. TIY!

    Sorry for the delay in posting the code in question (minus my experiments). Here's the code, broken into two more posts:

    <head>
    
    <script type="text/javascript" language="JavaScript">
    //<!--
    function submitForm(){
    	document.transfer_art.submit();
    }
    function toggleLayer(whichLayer)
    {
    
    if (document.getElementById)
    {
    	// this is the way the standards work
    	var style2 = document.getElementById(whichLayer).style;
    	style2.display = style2.display? "":"block";
    }
    else if (document.all)
    {
    	// this is the way old msie versions work
    	var style2 = document.all[whichLayer].style;
    	style2.display = style2.display? "":"block";
    }
    else if (document.layers)
    {
    	// this is the way nn4 works
    	var style2 = document.layers[whichLayer].style;
    	style2.display = style2.display? "none":"block";
    }
    }
    
    function searchHelp(theURL) {
    	newwindow = window.open(theURL, '', 'height=400, width=300, toolbar=no, scrollbars=yes, menubar=no, statusbar=no');
    	if (window.focus) {newwindow.focus()}
    }
    
    //-->
    </script>
    
    </head>
    <body>
    
    <?php
    
    
    //Call up onnection info for DB.
    require ('scripts/db.php');
    
    //Replace old codes.
    function convertCodes($input) {
    	$search = array("L1", "2", "3", "4", "5", "RIP", "RIC", "RAC", "1");
    	$replace = array("AL", "SS", "SCI", "MTH,SCI", "MTH", "IP", "IC", "AC", "AL");
    	$return = str_replace($search, $replace, $input);
    	return $return;
    }
    
    //Enable searching of old codes as well as new ones.
    function convertCodesReverse($input) {
    	$search = array("1", "2", "3", "4", "5", "RIP", "RIC", "RAC", "1", "2", "3", "4", "5", "RIP", "RIC", "RAC");
    	$replace = array("AL", "SS", "SCI", "MTH,SCI", "MTH", "IP", "IC", "AC", "al", "ss", "sci", "mth,sci", "mth", "ip", "ic", "ac");
    	$return = str_replace($replace, $search, $input);
    	return $return;
    }
    
    //The following SQL statements are used to populate the drop-down lists for
    //State, City and School.
    
    $sql_state = "SELECT DISTINCT state FROM transfer_articulation ORDER BY state";
    
    $sql_city = "SELECT DISTINCT city FROM transfer_articulation WHERE state = '" . strip_tags(stripslashes($_GET['state'])) . "' ORDER BY city";
    
    $sql_school = "SELECT DISTINCT sbgi_desc FROM transfer_articulation WHERE state = '" . strip_tags(stripslashes($_GET['state'])) . "' ORDER BY sbgi_desc";
    
    //This list of IF statements makes the Advanced search possible. A search criteria
    //will be included if and only if the user inputs into its corresponding
    //input box, otherwise it is left empty and doesn't affect the SQL statement.
    
    if(!empty($_GET['subject'])){
    	$subject = "AND (SUBJ_UNIV LIKE '%" . strip_tags(stripslashes($_GET['subject'])) . "%' OR SUBJ_TRANS LIKE '%" . strip_tags($_GET['subject']) . "%') ";
    
    }
    if(!empty($_GET['course'])){
    	$course = "AND (NUMB_UNIV LIKE '%" . strip_tags(stripslashes($_GET['course'])) . "%' OR NUMB_TRANS LIKE '%" . $_GET['course'] . "%') ";
    
    }
    if(!empty($_GET['title'])){
    	$title = "AND (TITLE_UNIV LIKE '%" . strip_tags(stripslashes($_GET['title'])) . "%' OR TITLE_TRANS LIKE '%" . $_GET['title'] . "%') ";
    
    }
    if(!empty($_GET['attributes'])){
    	$attributes = "AND ATTRIBUTES LIKE '%" . convertCodesReverse(strip_tags(stripslashes($_GET['attributes']))) . "%' ";
    
    }
    if(!empty($_GET['term'])){
    	$term = "AND TERM_ACCEPT LIKE '%" . strip_tags(stripslashes($_GET['term'])) . "%' ";
    
    }
    if(!empty($_GET['state'])){
    	$_SESSION['state_name'] = $_GET['state'];
    	$state = "STATE LIKE '" . strip_tags(stripslashes($_GET['state'])) . "' ";
    
    }
    if(!empty($_GET['city'])){
    	$city = "AND CITY LIKE '" . strip_tags(stripslashes($_GET['city'])) . "' ";
    
    }
    if(!empty($_GET['school'])){
    	$_SESSION['school_name'] = $_GET['school'];
    	$school = "AND SBGI_DESC LIKE '" . str_replace("'", "''", strip_tags(stripslashes($_GET['school']))) . "' ";
    
    }
    
    $_SESSION['subject'] = $subject;
    $_SESSION['course'] = $course;
    $_SESSION['title'] = $title;
    $_SESSION['attributes'] = $attributes;
    $_SESSION['term'] = $term;
    $_SESSION['state'] = $state;
    $_SESSION['city'] = $city;
    $_SESSION['school'] = $school;
    $_SESSION['school_name'] = $_GET['school'];
    $_SESSION['historic'] = $_GET['historic'];
    
    //The mssql_query built-in function is used here to pass the SQL to the
    //database and return the result set as an array.
    $rs_state = mssql_query($sql_state, $conn);
    $rs_city = mssql_query($sql_city, $conn);
    $rs_school = mssql_query($sql_school, $conn);
    
    
    ?>

    (Lotsa code, so one more post to go)

      ...And here is the second part of the code:

      <h2>Inputs</h2>
      
        <div id="search" style="width:490px">
        <div class="selection_area" style="float:left; height:300px; width:230px; margin-right:10px">
        <h4>Basic Search: </h4>Choose a state or province first, then an institution.  For international institutions, select "XX".<hr />
        <form action="<?=$_SERVER['PHP_SELF']?>" method="get" name="transfer_art">
        	<div style="margin-bottom:5px"><select name="state" onChange="javascript:document.transfer_art.submit();">
      		<option value="">State</option>
      		<? while($firstline = mssql_fetch_array($rs_state)){ ?>	
      			<? $line['state'] = rtrim($firstline['state']); ?>
      				<option value="<?=$line['state']?>" <? if($_GET['state'] == $line['state']){echo "selected";} ?>><?=$line['state']?></option>
      		<? } ?>
      	</select>
      	</div>
      
      	<!--<div style="margin-bottom:5px">2.&nbsp;<select name="city" onChange="javascript:document.transfer_art.submit();">
      		<option value="">City</option>
      		<? while($firstline = mssql_fetch_array($rs_city)){ ?>
      			<? $line['city'] = rtrim($firstline['city']); ?>
      				<option value="<?=$line['city']?>" <? if($_GET['city'] == $line['city']){echo "selected";} ?>><?=$line['city']?></option>
      		<? } ?>
      	</select>
      	</div>-->
      
      	<div style="margin-bottom:10px"><select name="school" onChange="javascript:document.transfer_art.submit();" style="margin-right:5px">
      		<option value="">Institution</option>
      		<? while($firstline = mssql_fetch_array($rs_school)){ ?>
      			<? $line['sbgi_desc'] = rtrim($firstline['sbgi_desc']); ?>
      				<option value="<?=$line['sbgi_desc']?>" <? if($_GET['school'] == $line['sbgi_desc']){echo "selected";} ?>><?=$line['sbgi_desc']?></option>
      		<? } ?>
      	</select>
      	<? if(empty($_GET['state'])) { ?>
      	<? } ?>
      	</div>
      
      	<? if(!empty($_GET['school'])) { ?>
      	<h4>Quick Buttons:</h4>
      	Return complete list of classes.
      	<hr />
      		<div style="margin-top:10px; margin-bottom:2px">
      			<input name="catalog" type="reset" value="Current Catalog" onClick="parent.location='<? echo "$_SERVER[PHP_SELF]?state=$_GET[state]&city=$_GET[city]&school=$_GET[school]&button_trans=Submit+Query"; ?>'" />
      		</div>
      		<div>
      			<input style="width:170px" name="button_historic" type="reset" value="View All Historic Records" onClick="parent.location='<? echo "$_SERVER[PHP_SELF]?state=$_GET[state]&school=$_GET[school]&button_trans=Submit+Query&historic=View+All+Historic+Records"; ?>'">
      		</div>
      	<? } ?>
      	</div>
      	<? if(!empty($_GET['school'])) { ?>
      
      
      		<div class="selection_area" style="float:right; width:200px; height:300px">
      			<h4>Advanced Search: </h4>Enter all or part of a word or number;  wildcards are not needed.<hr />
      
      			<table width="100%" border="0" cellpadding="1" cellspacing="0">
      				<tr>
      					<td><a href="help.html#subject" onclick="javascript:searchHelp('help.html#subject'); return false;">Subject:</a></td>
      					<td><input name="subject" type="text" size="12" /></td>
      				</tr>
      				<tr>
      					<td><a href="help.html#course" onclick="javascript:searchHelp('help.html#course'); return false;">Course:</a></td>
      					<td><input name="course" type="text" size="12" /></td>
      				</tr>
      				<tr>
      					<td><a href="help.html#title" onclick="javascript:searchHelp('help.html#title'); return false;">Title:</a></td>
      					<td><input name="title" type="text" size="12" /></td>
      				</tr>
      				<tr>
      					<td><a href="help.html#groupCodes" onclick="javascript:searchHelp('help.html#groupCodes'); return false;">Group Codes:</a></td>
      					<td><input name="attributes" type="text" size="12" /></td>
      				</tr>
      			</table>
      
      			<div style="margin-top:10px">
      				<input name="historic" type="checkbox" value="historic" <? if($_GET['historic']){echo "checked";} ?>/>
      					&nbsp;Include Historic Records in Search
      			</div>
      			<div style="margin-top:10px">
      				<input name="button_trans" type="submit" value="Submit Query">
      				&nbsp;
      				<input name="reset" type="reset" value="Reset" onClick="parent.location='<? echo "$_SERVER[PHP_SELF]"; ?>'"/>
      			</div>		
      		</div>
      		<div class="selection_area" style="position:absolute; top:207px; left:540px; width:150px; height:230px">
      		<h4>Links</h4>
      		<p>Looking for <a href="individualCourseLookup.php">Individual Course Lookups </a>?</p>
      		<p><a href="faq.htm">FAQs about Transferring Credits</a></p>
      
      		</div>
      	</div>
      
      	<div align="center" style="clear:both; margin-bottom:10px; padding-top:10px; border-top:thin solid black">
      
      		<? if(isset($_GET['button_trans'])) {?>
      			<!--<input name="new" type="reset" value="Open Results in New Page" onClick="window.open('new.php')"/>&nbsp;--><input name="new" type="reset" value="Show/Hide Search Form" onClick="toggleLayer('search')"/>
      		<? } ?>
      	</div>
      	<? } ?>
        </form>
      
        <?
        //An IF statement is used here to display the results only when the query is submitted.
        //It also forces the program to wait until the user has entered all input boxes
        //before constructing the SQL statement and passing it to the DB.
      
        if(isset($_GET['button_trans'])){
      
        //This nested IF statement is used to see if the user wishes to include historical
        //rows in the results. If so, the SQL is modified to include all term_accept results
      
        	if(isset($_GET['historic'])){
      		$sql_advanced = "SELECT SBGI_DESC, CITY, STATE, SUBJ_UNIV, SUBJ_TRANS, TITLE_TRANS, TITLE_UNIV, NUMB_UNIV, NUMB_TRANS, TERM_ACCEPT, ATTRIBUTES FROM transfer_articulation WHERE $state $school $subject $course $title $attributes $term ORDER BY CASE WHEN ASCII(SUBJ_TRANS)>57 then 0 else 1 end, SUBJ_TRANS, NUMB_TRANS";
      		}else{
      		$sql_advanced = "SELECT SBGI_DESC, CITY, STATE, SUBJ_UNIV, SUBJ_TRANS, TITLE_TRANS, TITLE_UNIV, NUMB_UNIV, NUMB_TRANS, ATTRIBUTES, TERM_ACCEPT FROM transfer_articulation a
      WHERE (TERM_ACCEPT =
              (select max(TERM_ACCEPT) from transfer_articulation b
                where b.SBGI_CODE=a.SBGI_CODE
                  and b.TITLE_TRANS=a.TITLE_TRANS
                  and b.NUMB_TRANS=a.NUMB_TRANS))
      		AND $state $school $subject $course $title $attributes $term ORDER BY CASE WHEN ASCII(SUBJ_TRANS)>57 then 0 else 1 end, SUBJ_TRANS, NUMB_TRANS";
      		}
      
      		$rs = mssql_query($sql_advanced, $conn);
      
      	?>
      <div class="data">
      <table width="100%" align="left" border="0" cellpadding="2" cellspacing="0">
      <tr align="center" bgcolor="#E0E0E0"><td colspan="4" style="color:#006600; font-weight:bold"><? echo stripslashes($_GET['school']); ?></td><td colspan="4" style="color:#006600; font-weight:bold">Local Equivalent</td></tr>
      	<tr bgcolor="#336633">
      		<th align="center" width="9%">Subject</th>
      		<th align="center" width="9%">Course</th>
      		<th align="center" width="27%">Title</th>
      		<th align="center" width="1%">&nbsp;</th>
      		<th align="center" width="9%">Subject</th>
      		<th align="center" width="9%">Course</th>
      		<th align="center" width="27%">Title</th>
      		<th align="center" width="8%">Group Codes</th>
      	</tr>
        	<? 	$count = 1;
      		$color1 = "#FFFFFF";
      		$color2 = "#FFFFCC";
      			while($line = mssql_fetch_assoc($rs)){
      				$row_color = ($count % 2) ? $color1 : $color2;	  			
      				echo "<tr bgcolor=" . $row_color . "><td align=left>"
      				.$line['SUBJ_TRANS']."</td><td align=left>"
      				.$line['NUMB_TRANS']."</td><td align=left>"
      				.$line['TITLE_TRANS']."</td><td bgcolor=#FFFFFF align=left>&nbsp;</td><td align=left>"
      				.$line['SUBJ_UNIV']."</td><td align=left>"
      				.$line['NUMB_UNIV']."</td><td align=left>"
      				.$line['TITLE_UNIV']."</td><td align=left>"
      				.convertCodes($line['ATTRIBUTES'])."</td></tr>";
      				$count++;
      			}
      	} 
      		mssql_close($conn);
      	?>
      </body>

        Assuming you are building the query string within your PHP code, you'll want to use the [man]urlencode/man function, e.g.:

        $urlSchool = urlencode($school);
        $urlState = urlencode($state);
        $url = "http://www.example.com/page.php?state=$state&smp;school=$school";
        
          NogDog;10883765 wrote:

          Assuming you are building the query string within your PHP code, you'll want to use the [man]urlencode/man function, e.g.:

          $urlSchool = urlencode($school);
          $urlState = urlencode($state);
          $url = "http://www.example.com/page.php?state=$state&smp;school=$school";
          

          Thanks for the quick reply, NogDog. (Code was a bit delayed in appearing after initial post).

            Given that the kind of URL that needs to be constructed would be something like:

            http://www.example.com/page.php?state=IA&city=&school=Iowa&#37;20State%20Univ%20Science%20%26%20Tech&button_trans=Submit+Query

            ...then would it make sense to place the following after all the $_SESSION lines, near the end of the large chunk of PHP code in my second post above, and just before the Inputs area?

            	//Ensure that school names containing an ampersand do not break the query in URL
            	$urlState = urlencode($state);
            	$urlCity = urlencode($city);
            	$urlSchool = urlencode($school);
            	$url = "http://www.example.com/page.php?state=$state&smp;city=$city&smp;school=$school&smp;button_trans=Submit+Query";
            

            Written that way, it seemed to have no effect, but then again I'm still pretty clueless when it comes to fixing someone else's PHP when it's as complex as this is (yeah, I know, it's probably less complex than I realize).

              I'm not sure where in the code you are actually setting and using this URL, so I don't really know what the problem may be. I don't see anything wrong with the code in your last post, but then I don't know what you do with $url after you set it.

                NogDog;10883792 wrote:

                I'm not sure where in the code you are actually setting and using this URL, so I don't really know what the problem may be. I don't see anything wrong with the code in your last post, but then I don't know what you do with $url after you set it.

                It looks like the URL in question is constructed in two places, soon after the"Quick Buttons" line (see code in context in this post above). First instance, for example:

                <? if(!empty($_GET['school'])) { ?> <h4>Quick Buttons:</h4> Return complete list of classes. <hr /> <div style="margin-top:10px; margin-bottom:2px"> <input name="catalog" type="reset" value="Current Catalog" onClick="parent.location='<? echo "$_SERVER[PHP_SELF]?state=$_GET[state]&city=$_GET[city]&school=$_GET[school]&button_trans=Submit+Query"; ?>'" /> </div> <div> <input style="width:170px" name="button_historic" type="reset" value="View All Historic Records" onClick="parent.location='<? echo "$_SERVER[PHP_SELF]?state=$_GET[state]&school=$_GET[school]&button_trans=Submit+Query&historic=View+All+Historic+Records"; ?>'"> </div> <? } ?> 

                Before posting here, I had made the following change in an experiment (using both urlencode and rawurlencode). But it only broke the URL even more and produced a 404:

                <? if(!empty($_GET['school'])) { ?> <h4>Quick Buttons:</h4> Return complete list of classes. <hr /> <div style="margin-top:10px; margin-bottom:2px"> <input name="catalog" type="reset" value="Current Catalog" onClick="parent.location='<? echo urlencode("$_SERVER[PHP_SELF]?state=$_GET[state]&city=$_GET[city]&school=$_GET[school]&button_trans=Submit+Query"); ?>'" /> </div> <div> <input style="width:170px" name="button_historic" type="reset" value="View All Historic Records" onClick="parent.location='<? echo urlencode("$_SERVER[PHP_SELF]?state=$_GET[state]&school=$_GET[school]&button_trans=Submit+Query&historic=View+All+Historic+Records"); ?>'"> </div> <? } ?> 

                This is what the URL ended up looking like (with the above urlencode change):

                http://www.example.com/foldername/%2Ffoldername%2Fpage.php%3Fstate%3DTX%26city%3D%26school%3DTexas+A+%26+M+Univ-Kingsville%26button_trans%3DSubmit%2BQuery

                In addition to the duplicate instance of the subfolder name (not mentioned in my posts above), other parts of the URL such as slashes and pluses are replaced.

                  You don't want to encode the entire URL, just the query string values, so you would need to break it up into something like:

                  <?php if(!empty($_GET['school']))
                  { ?> <h4>Quick Buttons:</h4> Return complete list of classes. <hr /> 
                  <div style="margin-top:10px; margin-bottom:2px">
                  <input name="catalog" type="reset" value="Current Catalog" onClick="parent.location='<?php echo
                  $_SERVER['PHP_SELF'] . "?state=" . urlencode($_GET[state]) . "&city=" .
                  urlencode($_GET[city]) . "&school=" . urlencode($_GET[school]) .
                  "&button_trans=Submit+Query"; ?>'" /> </div> <div>
                  <input style="width:170px" name="button_historic" type="reset" value="View All Historic Records" onClick="parent.location='<?php echo
                  $_SERVER[PHP_SELF] . "?state=" . urlencode($_GET[state]) . "&school=" . urlencode($_GET[school]) .
                  "&button_trans=Submit+Query&historic=View+All+Historic+Records"; ?>'"> </div> <?php } ?> 
                  
                    Write a Reply...