I've tried to narrow it down as best I can and the only conclusion I'm coming to is an URL rewrite as the exact same code works fine on a page that doesn't use it. I'm using the Windows URL Rewrite module and have set up a corresponding outbound rule. Now I'm trying to do an insert, but it inserts 4 times. I have the same result if I do a +1 update. Am I correct in thinking it's the Rewrite?
It definitely is the Rewrite, I just switched on the rewrite for the test page that was functioning correctly and I have the same result. This isn't something I have come across before, does anybody have any idea for a workaround?
Welcome to one of the many reasons I refuse to use Microsoft iiS... My idea for a workaround being "Install Apache". What language are you processing it with? What are you typing in AS the redirect? Is an equivalent to PHP's $_SERVER 'REQUEST_URI' or 'QUERY_STRING' available so you could maybe process the data being passed yourself instead of relying on the Engine? There's a reason in the software I write (for apache) I flat redirect every request not on my whitelist to a single index.php # redirect all 'unknown' requests to index.php RewriteEngine On RewriteRule !\.(gif|jpg|png|css|js|html|htm|txt|ico|zip|gz|rar|pdf|xml|wav|mp3|mp4|mpg|flv|swf|mkv|ogg|avi|webm|woff|svg|eot|ttf)$ index.php Code (markup): Then parse the request myself with this object: final class request { private static $data = []; public static function value($index = 0, $clean = false) { if ($index === true) { $index = 0; $clean = true; } if (count(self::$data) == 0) { $path = trim(str_replace(['\\','%5C'],'/', parse_url($_SERVER['REQUEST_URI'],PHP_URL_PATH) ),'/'); if (strpos($path,'..')) bomb('hackingDetected', 'uptreeFail'); $path = substr($path, strlen(HTTP_ROOT) - 1); self::$data = empty($path) ? ['introduction'] : explode('/',$path); foreach (self::$data as &$p) $p = urldecode($p); } return isset(self::$data[$index]) ? ( $clean ? cleanName(self::$data[$index]) : self::$data[$index] ) : false; } } Code (markup): Admittedly that's for processing 'friendly' URI's like http://www.ewiusb.com/breakdown -- where request::value() or request::value(0) would return 'breakdown'. As it doesn't rely on playing goofy tricks with trying to change directories into getData it's just more reliable, and easier to implement across different servers. (ngnix, apache, iis, etc). Really though it's hard to answer without you including either your web.config (given what you've said I'm assuming that's what you're working with) or explaining what you are redirecting and to where.
I know what you mean, I switched from Linux to Windows because I thought Linux was overly complicated. Oh, how wrong I was! Here is the actual rule from the Rewrite Generator in IIS: The original url was: /viewstory.php?id=123&title=This-Is-A-Title Here is how I was linking to it: //Title for friendly urls $title = preg_replace('/[^A-Za-z0-9-]+/', '-', $row['subject']); echo "<h2><a href='/" . $row['id'] . "/$title.html' title='" . ucwords(htmlallentities($row['subject'])) . "'>" . htmlallentities($row['subject']) . "</a></h2>"; PHP: Web.config outbound rule: <rule name="OutboundRewriteUserFriendlyURL1" preCondition="ResponseIsHtml1"> <match filterByTags="A, Form, Img" pattern="^(.*/)viewstory\.php\?id=([^=&]+)&(?:amp;)?title=([^=&]+)$" /> <action type="Rewrite" value="{R:1}{R:2}/{R:3}/" /> </rule> PHP: Rewrite: <rule name="RewriteUserFriendlyURL1" stopProcessing="true"> <match url="^([^/]+)/([^/]+)/?$" /> <conditions> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="viewstory.php?id={R:1}&title={R:2}" /> </rule> PHP: Redirect: (If anything, I think this is the culpret) <rule name="RedirectUserFriendlyURL1" stopProcessing="true"> <match url="^viewstory\.php$" /> <conditions> <add input="{REQUEST_METHOD}" pattern="^POST$" negate="true" /> <add input="{QUERY_STRING}" pattern="^id=([^=&]+)&title=([^=&]+)$" /> </conditions> <action type="Redirect" url="{C:1}/{C:2}" appendQueryString="false" /> </rule> PHP:
"simplification" can not only have diminishing returns, it can have the opposite of the intended effect. Sometimes it becomes possible for something to become so over-simplified it makes doing a complex task harder, if not impossible -- there's a reason in web development the term "false simplicity" is being bandied about more and more. What often LOOKS simpler or people are convinced is simpler just because some artsy fartsy type or magazine called it such... isn't. As to your problem, I think much of it might lie here: echo "<h2><a href='/" . $row['id'] . "/$title.html' title='" . ucwords(htmlallentities($row['subject'])) . "'>" . htmlallentities($row['subject']) . "</a></h2>"; Code (markup): Ignoring the pointless bandwidth wasting redundant title attribute (there is only ONE legitimate reason to make a TITLE identical to the content of a tag, and I don't see no accesskeys on that), the back-assward use of quotes and slow string additon you are making $title.html -- but I don't see your rewrite rules set up to handle that. Assuming any of those actually worked (which to be honest the whole thing seems WAY to complex for it's own good) I would have thought it would return ;title=whatever.html -- is your redirect handler set up to handle that .html "extension for nothing"? I mean, one of the whole reasons to play redirect games like this is to NOT use a .html extension -- well, unless you're doing 300-style redirects which you quite obviously are not. Still, there's a reason my code for that markup would be: echo ' <h2> <a href="', $row['id'], '/', $title, '" > ', htmlallentities($row['subject']), ' </a> </h2>'; Code (markup): Not sure why you'd be using the row ID as your directory either, I'd think that part would be near impossible to parse using IIs services. I could do it in Apache, but that would be a flat redirect of all requests EXCEPT a whitelist as per above. Not even sure how I'd go about that in IIs.
I see your point, although I'm not sure how adding .html would cause this issue. I'm not great when it comes to these things, but in the web.config, it's only set up to handle the first and second condition, so shouldn't it ignore the third condition which would be the .html? I'll take the .html out and see what happens.
It turned out not to be the case. I disabled the redirect rule to check if that was the issue but it wasn't. I did have AddThis on there which appends the URL with a tracker code, so I disabled that and I get the same result. The result is unpredictable though, sometimes it inserts 4 times, others 5 and my latest test was 8 almost like it's consistently inserting until the page finishes loading. Here is the code for the page (I know you're not gonna like it lol!), is there anything I am missing here? <?php require("config.php"); require("functions.php"); require("header.php"); //Get stories $statement = $dbconn->prepare(' SELECT stories.id, stories.dateposted, stories.subject, stories.body, images.name, stories.cat_id FROM stories LEFT OUTER JOIN images ON stories.id = images.story_id WHERE stories.id = :id '); $statement->execute([ ":id" => $_GET['id'] ]); while ($row = $statement->fetch(PDO::FETCH_ASSOC)) { $cat_id = $row['cat_id']; //Check if admin is logged in, if so, show the admin box if(check_login()) { echo "<div id='options_box'>Actions:<ul>"; echo "<li class='add'><a href='/addimages.php?id=" . $_GET['id'] . "'> Add Images </a></li>"; echo "<li class='delete'><a href='/deletestory.php?id=" . $_GET['id'] . "'> Delete Story </a></li>"; echo "<li class='edit'><a href='/editstory.php?id=" . $_GET['id'] . "'> Edit Story </a></li>"; echo "<ul></div>"; }//End check login echo "<div class='story_holder'>"; //Title for friendly urls $title = preg_replace('/[^A-Za-z0-9-]+/', '-', ucwords($row['subject'])); //echo "<h2><a href='/$row[id]/$title.html' title='" . $row['subject'] . "'>" . $row['subject'] . "</a></h2>"; echo "<h2><a href='/$row[id]/$title.html' title='" . ucwords(htmlallentities($row['subject'])) . "'>" . htmlallentities($row['subject']) . "</a></h2>"; echo "<p class='date'>"; echo date("D jS F Y g.iA", strtotime($row['dateposted'])) . "<br/>"; echo "</p>"; // query to show current images $statement = $dbconn->prepare(" SELECT * FROM images WHERE story_id = :story_id "); $statement->execute([ "story_id" => $_GET['id'] ]); $imagerow = $statement->fetchAll(PDO::FETCH_ASSOC); if (empty($imagerow)){ //Was going to show generic image }else { //Check if admin is logged in foreach ($imagerow AS $imagerow){ echo "<div class='img_wrapper'>"; echo "<img src='/images/" . $imagerow['name'] . "' style=\"max-width:490px;margin-bottom:1em;border:1px solid silver;padding:3px;\">"; if(check_login()) { echo "<div class='img_action'><a href='/deleteimage.php?image_id=" . $imagerow['id'] . "&story_id=" . $_GET['id'] . "'><img src='/images/basic/cancel.gif' alt='Delete?'></a></div>"; } echo "</div>"; } } $title = htmlallentities(ucfirst($title)); ?> <div id="shareContainer"> <h2>Share this story</h2> <label for="direct">Direct Link (Twitter/Facebook)</label> <input type="text" id="direct" value="http://www.horwichadvertiser.co.uk/<?php echo "" . $row['id'] . "";?>/<?php echo $title;?>.html" onclick="select()" /><br /> <label for="blog">HTML (Websites & Blogs)</label> <input type="text" id="blog" value='<a href="http://www.horwichadvertiser.co.uk/<?php echo "" . $row['id'] . "";?>/<?php echo $title;?>.html" title="<?php echo "" . $row['subject'] . "";?>" target="_blank"><?php echo "" . $row['subject'] . "";?></a>' onclick="select()" /><br /> <label for="forum">BB (Forums)</label> <input type="text" id="forum" value='[url=http://www.horwichadvertiser.co.uk/<?php echo "" . $row['id'] . "";?>/<?php echo $title;?>.html]<?php echo "" . $row['subject'] . "";?>[/url]' onclick="select()" /> </div> <?php $text = nl2br(htmlallentities($row['body'])); //print iconv('UTF-8', 'ASCII//TRANSLIT', $text); //"Mystring " echo "$text</div>"; ?> <div class="fb-comments" data-href="http://www.horwichadvertiser.co.uk/<?php echo $_GET['id'];?>/<?php echo $_GET['title'];?>" data-width="487" data-numposts="100" data-colorscheme="light"></div> <div id="fb-root"></div> <script>(function(d, s, id) { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; js.src = "//connect.facebook.net/en_US/all.js#xfbml=1&appId=424833514225219"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));</script> <?php }//End while loop //Add view //Add story views INSERT INTO story_views (storyid, cat_id) VALUES (:storyid, :catid) $statement = $dbconn->prepare(" INSERT INTO story_views (storyid, cat_id) VALUES (:id, :cat_id) "); $statement->execute([ ":id" => $_GET['id'], ":cat_id" => $cat_id ]); echo "<pre>"; print_r($statement->errorInfo()); echo "</pre>"; require("/footer.php"); ?> PHP:
Sorry I forgot to add, with regards to Apache, I'm running Plesk which doesn't support Apache as of version 10.