Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
itskage
Aug 26, 2003


I have a Dev out for a couple weeks with some family issues so I am tackling some of our website bugs to help the web team while they are crunching on a project.

Now I am not a PHP guy. I tend to live in Microsoft land.

So anyway this is blowing my mind:

code:
$var = $_GET['value'];

if (!$var){
    // Do something
};
Doesn't work.

code:
$var = $_GET['value'];

if (!$var || $var == ''){
    // Do something
};
No dice.


code:
$var = $_GET['value'];

if (!$var || $var == '' || $var == 'undefined'){
    // Do something
};
works?!

Let's var_dump that $var:

Var_dump posted:

string 'undefined' (length=9)

The value of the get data is literally a string with the value 'undefined'? Let's go check out what the gently caress it is that's calling this.

code:
var x = field('.item').find('.select_some_value:selected').val();

 var data = 'production_code='+x;

  // Make AJAX request
  $.ajaxRequest('/index.php?dispatch=thing_im_working_on&' + data, {
    hidden: true,
    full_render: 0,
    result_ids: 'result'+result,
    skip_result_ids_check: true
  });
So it's a radio button. Uses AJAX to call the PHP I'm using and update some stuff based on the selection. I'm trying to handle what happens if they select nothing, or if say their client is loving up and sends the request with nothing.

Why the hell is my GET data set to a string with the value undefined and not actually a null string?

Edit: Another dev is suggesting I try:
code:
if (!array_key_exists('var', $_GET)) { do something );
Will see...

Edit2: Yup that works. Still makes no sense why == 'undefined' works. gently caress web development. gently caress loosely typed languages. :colbert:

itskage fucked around with this message at 16:50 on Jan 28, 2015

Adbot
ADBOT LOVES YOU

itskage
Aug 26, 2003


Mogomra posted:

If x is undefined in javascript, it will be converted to the string 'undefined' when it's added to the data variable. Everything is working as intended. :pwn:

But for real,
code:
var x = field('.item').find('.select_some_value:selected').val() || '';

Using your suggestion works pretty well. Thanks a lot.

itskage
Aug 26, 2003


I'm actually having a problem I started looking into at the end of the day yesterday with the PHP SoapClient. Trying to extend it from inside of a namespace. It's possible I'm not familiar enough with PHP namespaces and its global classes, but the basic situation looks like this:

code:
<?php namespace address;

  class street {

   }

  // etc

  class address extends \SoapClient {

    private static $classmap = array(
      'street' = > 'street'
      // etc
     );

    public function address ($wsdl = $wsdl_url, $options = array(
      "trace" => 0,
      "exception" => 0,
      "login" => $login,
      "password" => $password,)) {
      foreach(self::$classmap as $key => $value) {
                if(!isset($options['classmap'][$key])) {
                    $options['classmap'][$key] = $value;
                }
            }
            parent::__construct($wsdl, $options);
      }
  }
So now when it runs I'm getting:

quote:

SoapClient::SoapClient() [<a href='soapclient.soapclient'>soapclient.soapclient</a>]: Invalid parameters Line: 19 File:address.php

If I remove the name space it runs fine. If I remove the \ from SoapClient then it looks for SoapClient within the namespace and throws an error. Some googling suggests I can take off the \ and then at the top declare an alias. use \SoapClient as SoapClient but that gives me the same error about parameters. (which makes sense).

The problem seems to be that the $wsdl parameter isn't being passed correctly. It even confuses php storm. From where I'm calling it:

code:
include_class('address.php');

$client = new address\address();
PHP Storm is telling me that it's missing the required $wsdl parameter... but I have that parameter defaulting.

So either it's something stupid with the $wsdl parameter, or the $classmap is getting screwed up in the name space as well. Most of this was done with wsdl2php and was okay until we realized that some of the services use the same names for classes and it was causing issues if you used them within the same scope.

itskage
Aug 26, 2003


Yeah in the actual code I'm setting it there, a la $wsdl = "http://x.com/address.svc/wsdl"

I just tried new address\address("http://x.com/address.svc/wsdl"), just to see what happens, but now I'm getting an "Unauthorized Line" error. The line it references is the return line on the function I'm actually using. Very odd.

Edit: I put a break point on the foreach in the address constructor. If I don't pass the wsdl manually like so: new address\address() then it never even hits the for each. If I remove the namespace, take the \ out, and then call new address() it hits the for each.

It's kind of like when I'm using the namespace and going address extends \SoapClient then the public function address isn't actually being recognized as my constructor/new method.

Fake edit: Googled after typing that out and it looks like it my be a php version issue.

http://php.net/manual/en/language.oop5.decon.php

quote:

<?php
namespace Foo;
class Bar {
public function Bar() {
// treated as constructor in PHP 5.3.0-5.3.2
// treated as regular method as of PHP 5.3.3

So we're above 5.3.3 so this makes sense... But then why the hell does it work at all? Unless that part is only true when using namespaces?

Edit2: Yup!

quote:

For backwards compatibility, if PHP 5 cannot find a __construct() function for a given class, and the class did not inherit one from a parent class, it will search for the old-style constructor function, by the name of the class. Effectively, it means that the only case that would have compatibility issues is if the class had a method named __construct() which was used for different semantics.

Unlike with other methods, PHP will not generate an E_STRICT level error message when __construct() is overridden with different parameters than the parent __construct() method has.

As of PHP 5.3.3, methods with the same name as the last element of a namespaced class name will no longer be treated as constructor. This change doesn't affect non-namespaced classes.


Edit3: Still getting bodied by namespaces. Any attempt to return a value from the extended SoapClient results in it not being able to find the class to return.
Using the example I have above. Say on the address class I have a function that returns the street class:

code:

	/*
         * @param parms $parameters
         * @return street
         */
        public function getStreet(parms $parameters) {
            return $this->__soapCall('getStreet', array($parameters),       array(
                    'uri' => 'http://schema.com/services',
                    'soapaction' => ''
                )
            );
        }
I will get "Class 'street' not found" probably because working on the extended SoapClient does something when it's checking the datatype to return and uses the global namespace for that. gently caress.

itskage fucked around with this message at 15:32 on May 1, 2015

itskage
Aug 26, 2003


v1nce posted:

I don't know if this is useful to you or not because there was so little to go on, but here's something:
http://stackoverflow.com/questions/4807171/php-soapclient-example-using-typemap-option

Thanks for this. It lead me in the right direction.

So basically my example above should have been:

code:
class address extends \SoapClient {

    private static $classmap = array(
      'street' = > 'address\\street'
      // etc
     );
Where the 'street' => 'street' mapping is prefixed with the address namespace. That's all there was to that.

Going to say I was not happy at first because that is a lot of updating to do. The just one of the 6 WSDL classes I need to update has 181 types on the classmap. But the I saw that I can actually run the wsdl2phpgenerator with a namespace flag and it will do this for us.

So this was kind of a pointless endeavor, but at least now I'll understand everything that's happening, including the quirks.

itskage
Aug 26, 2003


Cross posting this from the general thread:

I'm really enjoying eslint. When we started a node.js project I was able to configure very specific rules for it, pass them to my team, and then enforce them by making our CI run the linter and fail any commits that don't pass. It's a nice extra layer of tidyness and some safety in the javascript world, without going to something like typescript.

Is there anything as robust and customizable for PHP? I'd love to run some of the PHP stuff we still maintain through something like this, but I can't find anything that lets you be quite as specific in the settings, or also played nice with IDEs and CIs.

itskage
Aug 26, 2003


I used to use PHPStorm, but for the past 4 months I've been writing PHP almost daily in VSCode and I love it. Its JS support is fantastic if you're doing frontend stuff at all too, so it's bonus. And I also frequently switch to C# or node for some other services we manage and I like having a consistent IDE for it.

I have a tutorial I give to new hires for setting up nginx+php on windows, with VSCode and the PHP extensions for debugging in VSCode, PHP Unit, Composer, NPM and Webpack (and now with setting up PHP CS+MD thanks to this thread). I'll have to remove some company specfic stuff from the thread, but if anyone's interested I can post it here.

PHP in VSCode does have some issues where it "jams" up when debugging and it's trying to lint with CS or MD. But usually you just hit stop and restart the debugger.

itskage fucked around with this message at 15:23 on Mar 15, 2018

itskage
Aug 26, 2003


Ahh shoot I've been posting all night and forgot about your replies today. I should be able to post it tomorrow night.

itskage
Aug 26, 2003


This has become less of a "copy the guide from these mark down files" and more of a "Retype the jist of stuff into a forums post". Sorry but there's more company specific stuff in them than I remembered, and it was quite long and I was fighting with the lack of md formatting, so I cut it down a lot, and now it is like a text dump, but hopefully it's useful to someone.

To be clear this is for windows.

Install NGINX
  1. Download the NGINX/Windows binaries http://nginx.org/en/download.html
  2. Make a folder for nginx and unzip there E.g.: C:\nginx\
  3. Run nginx.exe to start. You can verify installation by going to http://localhost/ and you should get the nginx welcome page.

Some notes:
If you have something else listening on 80 (XAMPP IIS Express) then you need to stop it or configure NGINX to listen on another port.
To stop NGINX after running it this way you have to taskkill it.

Install PHP
  1. Download the PHP binaries for windows. http://php.net/downloads.php
  2. Extract them to a folder. E.g.: C:\php\
  3. Start PHP and listen on 9000 `c:\PHP\php-cgi.exe -b 127.0.0.1:9000

Test PHP
In nginx's folder /conf/nginx.conf un-comment the php location, and alter the SCRIPT_FILENAME per below.
code:
     # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000

     location ~ \.php$ {
       root           html;
       fastcgi_pass   127.0.0.1:9000;
       fastcgi_index  index.php;
       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
       include        fastcgi_params;
     }
- This is telling NGINX that any requests ending in .php should be forwarded to PHP and for PHP to execute the file at $document_root + relative path to the file.
- Add a test phpfile to the nginx root E.g.: nginx/html/phpinfo.php with
code:
<?php phpinfo();
- Restart the nginx process to load the changes.
- Now you should see php info when you navigate to http://localhost/phpinfo.php

Tweaking configs
Now I have a bunch of poo poo about tweaking nginx.conf, sites-available/yourside.conf and php.ini for our CMS and whatnot, that I won't touch here. NGINX is like apache with sites-availabe and sites-enabled and with that above snippet getting phpinfo working you should be able to figure it out, otherwise just ask.

Some stuff I will mention:
- Setup your SSL and hostnames if you are developing with https (which I don't know why you wouldn't in tyool 2018)
- Change your the root for your site in NGINX unless you plan to workout of C:\nginx\html or wherever you installed it.
- in php.ini be sure to uncomment any extensions you need (and ensure you have the .dlls for them in php\ext\) so like if you need curl just uncomment extension=php_curl.dll in the ini.
- Xdebug will be needed to debug in VSCode (does any IDE not need this?): dump your phpinfo() output into https://xdebug.org/wizard.php and it should result in you downloading a dll that you put in php\ext\ and then referencing in php.ini (zend_extension = php_xdebug-2.5.4-7.0-vc14-x86_64.dll)
- Confgure Xdebug in PHP ini. You can google a bunch of the options but minium I do this for local development:
code:
[debug]
; Always xdebug
xdebug.remote_autostart=1 
; Enable...
xdebug.remote_enable=1
; Debug at the start of every request
xdebug.remote_mode=req
; What host to send debugging info to (your local)
xdebug.remote_host=127.0.0.1
; Port to connect to VSCode on... be sure this is not your PHP port!
xdebug.remote_port=9001
; Auto start can be disabled and this can be enabled if you want to use a browser extension to enable/disable debugging, (from any machine even) but since it's local I don't see the point.
xdebug.remote_connect_back=0
Working files
Next I have directions about git cloning our projects down. It should be the same. Clone yours to where you set your NGINX site's root, (I'll just use C:\nginx\html\ for this). Once done or if starting from scratch you can open a workspace in vscode there. (ctrl+k ctrl+o). VSCode also has bulit in terminal (ctrl+`) so you can just run git commands there, or use the built in commands (F1 to open the command pallet, type git clone, follow prompts).

At this point if you have your site in the folder and all of your site's configs and any database connections or anything configured then your site should run under localhost (or if you need a hostname to serve multiple sites from one CMS like I do you can overwrite your hosts file).

Last thing is unless you want to run NGINX and PHP locally as a service (I don't) you will want something to start and stop them, so here's a bat file that kills any running nginx and php processes and then restarts them (and leaves the window open).
code:
@ECHO OFF

taskkill /f /IM nginx.exe
cd c:\nginx
start nginx

taskkill /f /IM php-cgi.exe
cd c:\php

Echo running php...
c:\php\php-cgi.exe -b 127.0.0.1:9000
You can add a task in vscode if you'd rather run that from the IDE.

Anyway that's the most rudimentary way to run NGINX and PHP locally on windows.


VS Code part:
Basic PHP support:
You still need PHP downloaded even if not running it locally because you will need to point your extensions at the .exe.
Microsoft will recommend you the: https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-pack which includes the debugger and IntelliSense, but I wasn't big on the intelliSense. Fortunatley you can get them seperately, so for me I just grab the debugger. https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug
Then instead I use Intelephense: https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client
Now you have to add some user/workspace settings (ctrl+,). I recommend putting these php specific things into workspace so they aren't trying to phpmd my node.js projects or dotnet core.
code:
    "php.suggest.basic": true, (disabling this will stop the double suggestions from the native VSCode's and the intelephens/IntelliSense)
    "php.validate.enable": true,
    "php.validate.run": "onSave", (I haven't tryied onType, but I'm constantly hitting ctrl+s from years of doing this so...)
    "php.validate.executablePath": "C:\\php\\php.exe", (or wherever you installed)
VS Code is pretty good and will autocomplete options and what not if you want to play with the settings.

Debugging:
This should work now, so open the debugging pane (ctrl+shift+d) then up at the top you have some profiles. Click the gear to get started and edit some:
Here's the most basic:
code:
 
     {
            "name": "Local",
            "type": "php",
            "request": "launch",
            "port": 9001,
            "pathMappings": {
                "C:/nginx/html/" : "${workspaceRoot}"
            }
        },
Listens on port 9001 for xdebug. Just set a break point in your index.php or wherever is easy to test, hit F5 to start debugging the current profile, and then open that page in a browser and it should halt on your breakpoint.

I keep four profiles: local, remote (to debug a test server), tests (to halt on exceptions when running a bunch of tests), and current file (which I think is built in and is quite useless to me since we use a CMS with a dispatcher).

Composer:
https://getcomposer.org/download/ (recommend just using the EXE)
There is a VS Code extension. You don't need it, but it adds composer commands to the command pallet, and will validate your composer.json so I recommend it: https://marketplace.visualstudio.com/items?itemName=ikappas.composer
Back to workspace settings (crtl+,):
code:
    "composer.enabled": true,
    "composer.executablePath": "C:\\ProgramData\\ComposerSetup\\bin\\composer.bat",
If you have a composer.json in your project. You should be able to run composer install now (ctrl+` composer install to do it from the command line, or F1 composer install to do it from the command pallet). If not you will need to make one (and the necessary git/vcs exclusions).

Anyway add and run composer install/update and it should install PHPMD, PHPCS, and PHPUnit with a Code coverage plugin.
code:
    "require-dev": {
        "phpmd/phpmd" : "@stable",
        "squizlabs/php_codesniffer": "3.*",
        "phpunit/phpunit": "@stable",
        "phpunit/php-code-coverage": "@stable"
    },
You can generate xml for PHPMD and PHPCS rules easily here with this:
http://edorian.github.io/php-coding-standard-generator/#phpcs
You can even paste your XML back in later if you need to make adjustments

PHPCS: https://marketplace.visualstudio.com/items?itemName=ikappas.phpcs
This is the less finicky of the two linters.
Workspace settings:
Where to find the rules to enforce, and where the cs script is. You're SUPPOSED to be able to go to vendor/bin/phpcs for this instead of the full path but that never worked for some reason.
code:
    "phpcs.standard": ".\\phpcs.xml",
    "phpcs.executablePath": "vendor/squizlabs/php_codesniffer/bin/phpcs",
PHPMD: https://marketplace.visualstudio.com/items?itemName=ecodes.vscode-phpmd
There's a couple of these. As of about August of last year that one was the best one, but PHPMD still tends to jam up sometimes on large projects/files espcially when the debugger is running.
If you notice this, just restart the debugger. You can also see at the bottom stuff like "PHP is linting document..." with a spinner and it hangs. Usually restart the debugger fixes that too.
What I think is the process to lint is getting locked behind an actual request you want to debug for some reason, and even though you finish in the GUI, VSCode debugger isn't communicating that back, and it just gets stuck. If someone knows better, by all means, please share.
code:
    "phpmd.verbose": true,
    "phpmd.rules": ".\\phpmd.xml",
If you don't use verbose then it doesn't tell you what rule the line is violating.

Once you get those setup you can see what horrors are lurking in the code base you've inherited:
The function getConfigJSONResponse() has an NPath complexity of 1773066240. The configured NPath complexity threshold is 200.
The function getConfigJSONResponse() has a Cyclomatic Complexity of 74. The configured cyclomatic complexity threshold is 10.
:allears:

PHP Unit: https://marketplace.visualstudio.com/items?itemName=emallin.phpunit
Only needed if you want to run tests in VSCode while developing.
code:
    "phpunit.execPath": ".\\vendor\\bin\\phpunit.bat",
    "phpunit.args": [
        "--configuration", ".\\Tests\\phpunit.xml",
        "--coverage-text=.\\Tests\build\\coverage.txt"
    ],
    "phpunit.preferRunClassTestOverQuickPickWindow": false, // Default false
Not going to cover the phpunit.xml and setup of tests unless someone needs it.

To run tests in VS Code hit F1 and type PHPUnit Test with nothing open or selected will run all tests in the listed configuration.
With a test file/case open and selected hit F1 and type PHPUnit Test to run all tests for that file.
To run a specific test, double clock the function to highlight it, then do the F1 type PHPUnit Test (this autocompletes after the first time) and then it will just run that single test.


Webpack:
Be sure you've installed node and npm. I'm assuming you know what this is and how it works outside of VSCode. If not, ask but don't just add it because it's here.
Then open a terminal and npm install webpack, or I recommend using a package.json for your project:
code:
{
    "name": "",
    "version": "",
    "description": "",
    "dependencies": {
        "webpack": "3.10.0"
    }
}
Then just npm install.

Now you just make a webpack.config like any other webpack project, and the cool thing is you can just make some tasks to run this for you:
Under Tasks -> Configure Tasks...
You can add these:
code:
        {
            "label": "webpak build",
            "type": "shell",
            "command": "${workspaceRoot}/node_modules/.bin/webpack",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "args": [
                "--colors",
                "--progress"
            ]
        },
        {
            "label": "webpak watch",
            "type": "shell",
            "command": "${workspaceRoot}/node_modules/.bin/webpack",
            "args": [
                "--colors",
                "--progress",
                "--watch"
            ]
        }
First one just runs a build. But you can have that set as default so it auto builds whenever you run a build task which you can even have it do when you start the debugger.
2nd one adds --watch so it will auto update with any changes you make.

Probably would be better breaking it down into multiple posts (it's 3 different guides in our private github) but there you go.

Summary
Anyway it's a bit janky for PHP, but it works fine when you know the quirks. I think NGINX+php-fpm is better than apache-php and VS Code is the best IDE in general. So if this helps some people try it out then great.

If you're like me and jump between dotnet core and node as well as PHP (and frontend js) then it's nice just being in one IDE. It's not perfect (sometimes even for dotnet core I have to open VS2017), but I like it.

Finally one last thing is you can now write the js for your PHP site in here with ts which has fantastic vs code support.

itskage fucked around with this message at 22:47 on Mar 21, 2018

itskage
Aug 26, 2003


Anyone seem to have an issue with PHPUnit dying on a max time limit of 300 seconds? It seem to ignore php.ini settings and any set_time_limit/ini_set features. Our CI is is dying on it when running the full suite on new commits. Running it on just a few tests works fine, so it's like PHPUnit is trying to execute the tests with that time limit for the suite, which isn't going to work for this large project.

E: I think I found it. Going to be really sad if it's what I think it is.

itskage fucked around with this message at 17:00 on Jun 4, 2018

itskage
Aug 26, 2003


Someone had a set_time_limit set in a function that iterates over a lot of records. For the purposes of the unit test it doesn't matter, but once set it would stick and PHPUnit would use that for the rest of the test.


How do people handle this? I don't see an issue using set time limit for things that will take awhile and moving it beyond the 30 second default. I don't like the idea of configuring CI to be longer globally because something hanging can be caught in CI before it hits production.

The best idea I can think of is to have each phpunit's TestCase set_time_limit to default during setup. So that any classes or functions that use set time limit in other cases won't impede on others.


Edit: For the record we're adding tests to an existing 6 year old ball of mud project. It's a fun an interesting journey that's going about as well as you'd think something like that would go.

itskage fucked around with this message at 18:29 on Jun 4, 2018

itskage
Aug 26, 2003


raej posted:

Is it possible to take bones.php and stick it in my theme so add those functions? It might be more of a wordpress specific question...

Probably. So long as nothing in there is relying on something else that's missing.

e: I don't endorse this fix though.

itskage
Aug 26, 2003


Agrikk posted:

I am building an IIS-based PHP web site on AWS

But why??

itskage
Aug 26, 2003


If you're just playing around to learn, you'll get way more bang for your buck out of learning a better setup.

Depends on what your goals are I guess.

e: I posted a guide to running php+nginx on windows in here a bit ago if that's of any interest. But that's really for local dev. Production should go on linux.

itskage fucked around with this message at 06:48 on Dec 13, 2019

Adbot
ADBOT LOVES YOU

itskage
Aug 26, 2003


Yeah if they have that reaction to it you probably don't want it?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply